Compare commits
82 Commits
version-20
...
smusali/qr
Author | SHA1 | Date | |
---|---|---|---|
8daa8e1ca1 | |||
0f78db65a9 | |||
4e741416d8 | |||
87f3484be4 | |||
0b25c612c0 | |||
38356ac1dc | |||
f0619814f9 | |||
d09bee7bf9 | |||
81c22fa22a | |||
47a916ad5e | |||
4a41811465 | |||
8dbfafe612 | |||
b6160cf759 | |||
4118a34ed9 | |||
9f78d34719 | |||
21d5059876 | |||
4093b2b71f | |||
0d974dd0e1 | |||
0138aef70a | |||
d063fcb117 | |||
3e64409fdb | |||
ce96600adb | |||
e8c2aabad0 | |||
60e911baf8 | |||
a8067c1f0d | |||
f8ca498c77 | |||
489a680ff4 | |||
6c3a1795dd | |||
5b0cc3672b | |||
1ce482911b | |||
c869f3a3e2 | |||
2236eaccbc | |||
09fea420dd | |||
5c3295f4fd | |||
41de8f1191 | |||
22ee587e9f | |||
7c9659dd24 | |||
1ba734cc7b | |||
7c43c1a05b | |||
4230d8ee20 | |||
d590c1cdc4 | |||
ac843bb8ce | |||
71ba5be55f | |||
7358553333 | |||
d53d212377 | |||
9a39696367 | |||
6766b12bd1 | |||
c1404285bb | |||
8bba8422d7 | |||
ffcf8b110b | |||
894b4e3ca7 | |||
7c7957f160 | |||
36340d0960 | |||
9f9a71f3d6 | |||
0d0bb1a559 | |||
e3e1fbad3f | |||
91f0d31175 | |||
8af9eca24c | |||
1ee78ff1f2 | |||
618a61af04 | |||
44341f0224 | |||
444deae637 | |||
ba0e64d304 | |||
05fd539db5 | |||
3dd200dbe5 | |||
411ef239f6 | |||
25840ce04e | |||
bb64fb1130 | |||
5d5938c412 | |||
d8de60b053 | |||
b4a3b266b3 | |||
65c02c9ad5 | |||
e4d8612088 | |||
c2b26718f6 | |||
300901e93f | |||
33386b126c | |||
1bdc0b5e65 | |||
a308cfedf3 | |||
3236f57f7b | |||
0a4792cf95 | |||
6af85b002f | |||
30d2c4fcc6 |
@ -11,31 +11,37 @@ metadata:
|
||||
entries:
|
||||
- model: authentik_core.token
|
||||
identifiers:
|
||||
identifier: %(uid)s-token
|
||||
identifier: "%(uid)s-token"
|
||||
attrs:
|
||||
key: %(uid)s
|
||||
user: %(user)s
|
||||
key: "%(uid)s"
|
||||
user: "%(user)s"
|
||||
intent: api
|
||||
- model: authentik_core.application
|
||||
identifiers:
|
||||
slug: %(uid)s-app
|
||||
slug: "%(uid)s-app"
|
||||
attrs:
|
||||
name: %(uid)s-app
|
||||
name: "%(uid)s-app"
|
||||
icon: https://goauthentik.io/img/icon.png
|
||||
- model: authentik_sources_oauth.oauthsource
|
||||
identifiers:
|
||||
slug: %(uid)s-source
|
||||
slug: "%(uid)s-source"
|
||||
attrs:
|
||||
name: %(uid)s-source
|
||||
name: "%(uid)s-source"
|
||||
provider_type: azuread
|
||||
consumer_key: %(uid)s
|
||||
consumer_secret: %(uid)s
|
||||
consumer_key: "%(uid)s"
|
||||
consumer_secret: "%(uid)s"
|
||||
icon: https://goauthentik.io/img/icon.png
|
||||
- model: authentik_flows.flow
|
||||
identifiers:
|
||||
slug: %(uid)s-flow
|
||||
slug: "%(uid)s-flow"
|
||||
attrs:
|
||||
name: %(uid)s-flow
|
||||
title: %(uid)s-flow
|
||||
name: "%(uid)s-flow"
|
||||
title: "%(uid)s-flow"
|
||||
designation: authentication
|
||||
background: https://goauthentik.io/img/icon.png
|
||||
- model: authentik_core.user
|
||||
identifiers:
|
||||
username: "%(uid)s"
|
||||
attrs:
|
||||
name: "%(uid)s"
|
||||
password: "%(uid)s"
|
||||
|
@ -2,7 +2,7 @@
|
||||
from django.test import TransactionTestCase
|
||||
|
||||
from authentik.blueprints.v1.importer import Importer
|
||||
from authentik.core.models import Application, Token
|
||||
from authentik.core.models import Application, Token, User
|
||||
from authentik.core.tests.utils import create_test_admin_user
|
||||
from authentik.flows.models import Flow
|
||||
from authentik.lib.generators import generate_id
|
||||
@ -45,3 +45,9 @@ class TestBlueprintsV1ConditionalFields(TransactionTestCase):
|
||||
flow = Flow.objects.filter(slug=f"{self.uid}-flow").first()
|
||||
self.assertIsNotNone(flow)
|
||||
self.assertEqual(flow.background, "https://goauthentik.io/img/icon.png")
|
||||
|
||||
def test_user(self):
|
||||
"""Test user"""
|
||||
user: User = User.objects.filter(username=self.uid).first()
|
||||
self.assertIsNotNone(user)
|
||||
self.assertTrue(user.check_password(self.uid))
|
||||
|
@ -184,9 +184,9 @@ def apply_blueprint(self: MonitoredTask, instance_pk: str):
|
||||
instance: Optional[BlueprintInstance] = None
|
||||
try:
|
||||
instance: BlueprintInstance = BlueprintInstance.objects.filter(pk=instance_pk).first()
|
||||
self.set_uid(slugify(instance.name))
|
||||
if not instance or not instance.enabled:
|
||||
return
|
||||
self.set_uid(slugify(instance.name))
|
||||
blueprint_content = instance.retrieve()
|
||||
file_hash = sha512(blueprint_content.encode()).hexdigest()
|
||||
importer = Importer(blueprint_content, instance.context)
|
||||
|
@ -33,7 +33,7 @@ class TokenSerializer(ManagedSerializer, ModelSerializer):
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
if SERIALIZER_CONTEXT_BLUEPRINT in self.context:
|
||||
self.fields["key"] = CharField()
|
||||
self.fields["key"] = CharField(required=False)
|
||||
|
||||
def validate(self, attrs: dict[Any, str]) -> dict[Any, str]:
|
||||
"""Ensure only API or App password tokens are created."""
|
||||
|
@ -51,6 +51,7 @@ from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.admin.api.metrics import CoordinateSerializer
|
||||
from authentik.api.decorators import permission_required
|
||||
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import LinkSerializer, PassiveSerializer, is_dict
|
||||
from authentik.core.middleware import (
|
||||
@ -112,6 +113,30 @@ class UserSerializer(ModelSerializer):
|
||||
uid = CharField(read_only=True)
|
||||
username = CharField(max_length=150, validators=[UniqueValidator(queryset=User.objects.all())])
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if SERIALIZER_CONTEXT_BLUEPRINT in self.context:
|
||||
self.fields["password"] = CharField(required=False)
|
||||
|
||||
def create(self, validated_data: dict) -> User:
|
||||
"""If this serializer is used in the blueprint context, we allow for
|
||||
directly setting a password. However should be done via the `set_password`
|
||||
method instead of directly setting it like rest_framework."""
|
||||
instance: User = super().create(validated_data)
|
||||
if SERIALIZER_CONTEXT_BLUEPRINT in self.context and "password" in validated_data:
|
||||
instance.set_password(validated_data["password"])
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
def update(self, instance: User, validated_data: dict) -> User:
|
||||
"""Same as `create` above, set the password directly if we're in a blueprint
|
||||
context"""
|
||||
instance = super().update(instance, validated_data)
|
||||
if SERIALIZER_CONTEXT_BLUEPRINT in self.context and "password" in validated_data:
|
||||
instance.set_password(validated_data["password"])
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
def validate_path(self, path: str) -> str:
|
||||
"""Validate path"""
|
||||
if path[:1] == "/" or path[-1] == "/":
|
||||
|
@ -5,7 +5,6 @@ from typing import Any, Optional
|
||||
from uuid import uuid4
|
||||
|
||||
from deepmerge import always_merger
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.hashers import check_password
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.contrib.auth.models import UserManager as DjangoUserManager
|
||||
@ -33,6 +32,7 @@ from authentik.lib.models import (
|
||||
)
|
||||
from authentik.lib.utils.http import get_client_ip
|
||||
from authentik.policies.models import PolicyBindingModel
|
||||
from authentik.root.install_id import get_install_id
|
||||
|
||||
LOGGER = get_logger()
|
||||
USER_ATTRIBUTE_DEBUG = "goauthentik.io/user/debug"
|
||||
@ -217,7 +217,7 @@ class User(SerializerModel, GuardianUserMixin, AbstractUser):
|
||||
@property
|
||||
def uid(self) -> str:
|
||||
"""Generate a globally unique UID, based on the user ID and the hashed secret key"""
|
||||
return sha256(f"{self.id}-{settings.SECRET_KEY}".encode("ascii")).hexdigest()
|
||||
return sha256(f"{self.id}-{get_install_id()}".encode("ascii")).hexdigest()
|
||||
|
||||
def locale(self, request: Optional[HttpRequest] = None) -> str:
|
||||
"""Get the locale the user has configured"""
|
||||
|
@ -23,6 +23,7 @@ from authentik.flows.api.bindings import FlowStageBindingSerializer
|
||||
from authentik.flows.models import Flow
|
||||
from authentik.flows.planner import FlowPlan
|
||||
from authentik.flows.views.executor import SESSION_KEY_HISTORY, SESSION_KEY_PLAN
|
||||
from authentik.root.install_id import get_install_id
|
||||
|
||||
|
||||
class FlowInspectorPlanSerializer(PassiveSerializer):
|
||||
@ -51,7 +52,7 @@ class FlowInspectorPlanSerializer(PassiveSerializer):
|
||||
"""Get a unique session ID"""
|
||||
request: Request = self.context["request"]
|
||||
return sha256(
|
||||
f"{request._request.session.session_key}-{settings.SECRET_KEY}".encode("ascii")
|
||||
f"{request._request.session.session_key}-{get_install_id()}".encode("ascii")
|
||||
).hexdigest()
|
||||
|
||||
|
||||
|
@ -1,185 +1,40 @@
|
||||
"""Kubernetes Traefik Middleware Reconciler"""
|
||||
from dataclasses import asdict, dataclass, field
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from dacite.core import from_dict
|
||||
from kubernetes.client import ApiextensionsV1Api, CustomObjectsApi
|
||||
|
||||
from authentik.outposts.controllers.base import FIELD_MANAGER
|
||||
from authentik.outposts.controllers.k8s.base import KubernetesObjectReconciler
|
||||
from authentik.outposts.controllers.k8s.triggers import NeedsUpdate
|
||||
from authentik.providers.proxy.models import ProxyMode, ProxyProvider
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from authentik.outposts.controllers.kubernetes import KubernetesController
|
||||
from authentik.outposts.controllers.kubernetes import KubernetesController
|
||||
from authentik.providers.proxy.controllers.k8s.traefik_2 import Traefik2MiddlewareReconciler
|
||||
from authentik.providers.proxy.controllers.k8s.traefik_3 import (
|
||||
Traefik3MiddlewareReconciler,
|
||||
TraefikMiddleware,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TraefikMiddlewareSpecForwardAuth:
|
||||
"""traefik middleware forwardAuth spec"""
|
||||
|
||||
address: str
|
||||
# pylint: disable=invalid-name
|
||||
authResponseHeadersRegex: str = field(default="")
|
||||
# pylint: disable=invalid-name
|
||||
authResponseHeaders: list[str] = field(default_factory=list)
|
||||
# pylint: disable=invalid-name
|
||||
trustForwardHeader: bool = field(default=True)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TraefikMiddlewareSpec:
|
||||
"""Traefik middleware spec"""
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
forwardAuth: TraefikMiddlewareSpecForwardAuth
|
||||
|
||||
|
||||
@dataclass
|
||||
class TraefikMiddlewareMetadata:
|
||||
"""Traefik Middleware metadata"""
|
||||
|
||||
name: str
|
||||
namespace: str
|
||||
labels: dict = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TraefikMiddleware:
|
||||
"""Traefik Middleware"""
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
apiVersion: str
|
||||
kind: str
|
||||
metadata: TraefikMiddlewareMetadata
|
||||
spec: TraefikMiddlewareSpec
|
||||
|
||||
|
||||
CRD_NAME = "middlewares.traefik.containo.us"
|
||||
CRD_GROUP = "traefik.containo.us"
|
||||
CRD_VERSION = "v1alpha1"
|
||||
CRD_PLURAL = "middlewares"
|
||||
|
||||
|
||||
class TraefikMiddlewareReconciler(KubernetesObjectReconciler[TraefikMiddleware]):
|
||||
class TraefikMiddlewareReconciler(KubernetesObjectReconciler):
|
||||
"""Kubernetes Traefik Middleware Reconciler"""
|
||||
|
||||
def __init__(self, controller: "KubernetesController") -> None:
|
||||
super().__init__(controller)
|
||||
self.api_ex = ApiextensionsV1Api(controller.client)
|
||||
self.api = CustomObjectsApi(controller.client)
|
||||
self.reconciler = Traefik3MiddlewareReconciler(controller)
|
||||
if not self.reconciler.crd_exists():
|
||||
self.reconciler = Traefik2MiddlewareReconciler(controller)
|
||||
|
||||
@property
|
||||
def noop(self) -> bool:
|
||||
if not ProxyProvider.objects.filter(
|
||||
outpost__in=[self.controller.outpost],
|
||||
mode__in=[ProxyMode.FORWARD_SINGLE, ProxyMode.FORWARD_DOMAIN],
|
||||
).exists():
|
||||
self.logger.debug("No providers with forward auth enabled.")
|
||||
return True
|
||||
if not self._crd_exists():
|
||||
self.logger.debug("CRD doesn't exist")
|
||||
return True
|
||||
return False
|
||||
|
||||
def _crd_exists(self) -> bool:
|
||||
"""Check if the traefik middleware exists"""
|
||||
return bool(
|
||||
len(
|
||||
self.api_ex.list_custom_resource_definition(
|
||||
field_selector=f"metadata.name={CRD_NAME}"
|
||||
).items
|
||||
)
|
||||
)
|
||||
return self.reconciler.noop
|
||||
|
||||
def reconcile(self, current: TraefikMiddleware, reference: TraefikMiddleware):
|
||||
super().reconcile(current, reference)
|
||||
if current.spec.forwardAuth.address != reference.spec.forwardAuth.address:
|
||||
raise NeedsUpdate()
|
||||
if (
|
||||
current.spec.forwardAuth.authResponseHeadersRegex
|
||||
!= reference.spec.forwardAuth.authResponseHeadersRegex
|
||||
):
|
||||
raise NeedsUpdate()
|
||||
# Ensure all of our headers are set, others can be added by the user.
|
||||
if not set(current.spec.forwardAuth.authResponseHeaders).issubset(
|
||||
reference.spec.forwardAuth.authResponseHeaders
|
||||
):
|
||||
raise NeedsUpdate()
|
||||
return self.reconcile(current, reference)
|
||||
|
||||
def get_reference_object(self) -> TraefikMiddleware:
|
||||
"""Get deployment object for outpost"""
|
||||
return TraefikMiddleware(
|
||||
apiVersion=f"{CRD_GROUP}/{CRD_VERSION}",
|
||||
kind="Middleware",
|
||||
metadata=TraefikMiddlewareMetadata(
|
||||
name=self.name,
|
||||
namespace=self.namespace,
|
||||
labels=self.get_object_meta().labels,
|
||||
),
|
||||
spec=TraefikMiddlewareSpec(
|
||||
forwardAuth=TraefikMiddlewareSpecForwardAuth(
|
||||
address=(
|
||||
f"http://{self.name}.{self.namespace}:9000/"
|
||||
"outpost.goauthentik.io/auth/traefik"
|
||||
),
|
||||
authResponseHeaders=[
|
||||
"X-authentik-username",
|
||||
"X-authentik-groups",
|
||||
"X-authentik-email",
|
||||
"X-authentik-name",
|
||||
"X-authentik-uid",
|
||||
"X-authentik-jwt",
|
||||
"X-authentik-meta-jwks",
|
||||
"X-authentik-meta-outpost",
|
||||
"X-authentik-meta-provider",
|
||||
"X-authentik-meta-app",
|
||||
"X-authentik-meta-version",
|
||||
],
|
||||
authResponseHeadersRegex="",
|
||||
trustForwardHeader=True,
|
||||
)
|
||||
),
|
||||
)
|
||||
return self.get_reference_object()
|
||||
|
||||
def create(self, reference: TraefikMiddleware):
|
||||
return self.api.create_namespaced_custom_object(
|
||||
group=CRD_GROUP,
|
||||
version=CRD_VERSION,
|
||||
plural=CRD_PLURAL,
|
||||
namespace=self.namespace,
|
||||
body=asdict(reference),
|
||||
field_manager=FIELD_MANAGER,
|
||||
)
|
||||
return self.create(reference)
|
||||
|
||||
def delete(self, reference: TraefikMiddleware):
|
||||
return self.api.delete_namespaced_custom_object(
|
||||
group=CRD_GROUP,
|
||||
version=CRD_VERSION,
|
||||
namespace=self.namespace,
|
||||
plural=CRD_PLURAL,
|
||||
name=self.name,
|
||||
)
|
||||
return self.delete(reference)
|
||||
|
||||
def retrieve(self) -> TraefikMiddleware:
|
||||
return from_dict(
|
||||
TraefikMiddleware,
|
||||
self.api.get_namespaced_custom_object(
|
||||
group=CRD_GROUP,
|
||||
version=CRD_VERSION,
|
||||
namespace=self.namespace,
|
||||
plural=CRD_PLURAL,
|
||||
name=self.name,
|
||||
),
|
||||
)
|
||||
return self.retrieve()
|
||||
|
||||
def update(self, current: TraefikMiddleware, reference: TraefikMiddleware):
|
||||
return self.api.patch_namespaced_custom_object(
|
||||
group=CRD_GROUP,
|
||||
version=CRD_VERSION,
|
||||
namespace=self.namespace,
|
||||
plural=CRD_PLURAL,
|
||||
name=self.name,
|
||||
body=asdict(reference),
|
||||
field_manager=FIELD_MANAGER,
|
||||
)
|
||||
return self.update(current, reference)
|
||||
|
18
authentik/providers/proxy/controllers/k8s/traefik_2.py
Normal file
18
authentik/providers/proxy/controllers/k8s/traefik_2.py
Normal file
@ -0,0 +1,18 @@
|
||||
"""Kubernetes Traefik Middleware Reconciler"""
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from authentik.providers.proxy.controllers.k8s.traefik_3 import Traefik3MiddlewareReconciler
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from authentik.outposts.controllers.kubernetes import KubernetesController
|
||||
|
||||
|
||||
class Traefik2MiddlewareReconciler(Traefik3MiddlewareReconciler):
|
||||
"""Kubernetes Traefik Middleware Reconciler"""
|
||||
|
||||
def __init__(self, controller: "KubernetesController") -> None:
|
||||
super().__init__(controller)
|
||||
self.crd_name = "middlewares.traefik.containo.us"
|
||||
self.crd_group = "traefik.containo.us"
|
||||
self.crd_version = "v1alpha1"
|
||||
self.crd_plural = "middlewares"
|
183
authentik/providers/proxy/controllers/k8s/traefik_3.py
Normal file
183
authentik/providers/proxy/controllers/k8s/traefik_3.py
Normal file
@ -0,0 +1,183 @@
|
||||
"""Kubernetes Traefik Middleware Reconciler"""
|
||||
from dataclasses import asdict, dataclass, field
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from dacite.core import from_dict
|
||||
from kubernetes.client import ApiextensionsV1Api, CustomObjectsApi
|
||||
|
||||
from authentik.outposts.controllers.base import FIELD_MANAGER
|
||||
from authentik.outposts.controllers.k8s.base import KubernetesObjectReconciler
|
||||
from authentik.outposts.controllers.k8s.triggers import NeedsUpdate
|
||||
from authentik.providers.proxy.models import ProxyMode, ProxyProvider
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from authentik.outposts.controllers.kubernetes import KubernetesController
|
||||
|
||||
|
||||
@dataclass
|
||||
class TraefikMiddlewareSpecForwardAuth:
|
||||
"""traefik middleware forwardAuth spec"""
|
||||
|
||||
address: str
|
||||
# pylint: disable=invalid-name
|
||||
authResponseHeadersRegex: str = field(default="")
|
||||
# pylint: disable=invalid-name
|
||||
authResponseHeaders: list[str] = field(default_factory=list)
|
||||
# pylint: disable=invalid-name
|
||||
trustForwardHeader: bool = field(default=True)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TraefikMiddlewareSpec:
|
||||
"""Traefik middleware spec"""
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
forwardAuth: TraefikMiddlewareSpecForwardAuth
|
||||
|
||||
|
||||
@dataclass
|
||||
class TraefikMiddlewareMetadata:
|
||||
"""Traefik Middleware metadata"""
|
||||
|
||||
name: str
|
||||
namespace: str
|
||||
labels: dict = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TraefikMiddleware:
|
||||
"""Traefik Middleware"""
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
apiVersion: str
|
||||
kind: str
|
||||
metadata: TraefikMiddlewareMetadata
|
||||
spec: TraefikMiddlewareSpec
|
||||
|
||||
|
||||
class Traefik3MiddlewareReconciler(KubernetesObjectReconciler[TraefikMiddleware]):
|
||||
"""Kubernetes Traefik Middleware Reconciler"""
|
||||
|
||||
def __init__(self, controller: "KubernetesController") -> None:
|
||||
super().__init__(controller)
|
||||
self.api_ex = ApiextensionsV1Api(controller.client)
|
||||
self.api = CustomObjectsApi(controller.client)
|
||||
self.crd_name = "middlewares.traefik.io"
|
||||
self.crd_group = "traefik.io"
|
||||
self.crd_version = "v1alpha1"
|
||||
self.crd_plural = "middlewares"
|
||||
|
||||
@property
|
||||
def noop(self) -> bool:
|
||||
if not ProxyProvider.objects.filter(
|
||||
outpost__in=[self.controller.outpost],
|
||||
mode__in=[ProxyMode.FORWARD_SINGLE, ProxyMode.FORWARD_DOMAIN],
|
||||
).exists():
|
||||
self.logger.debug("No providers with forward auth enabled.")
|
||||
return True
|
||||
if not self.crd_exists():
|
||||
self.logger.debug("CRD doesn't exist")
|
||||
return True
|
||||
return False
|
||||
|
||||
def crd_exists(self) -> bool:
|
||||
"""Check if the traefik middleware exists"""
|
||||
return bool(
|
||||
len(
|
||||
self.api_ex.list_custom_resource_definition(
|
||||
field_selector=f"metadata.name={self.crd_name}"
|
||||
).items
|
||||
)
|
||||
)
|
||||
|
||||
def reconcile(self, current: TraefikMiddleware, reference: TraefikMiddleware):
|
||||
super().reconcile(current, reference)
|
||||
if current.spec.forwardAuth.address != reference.spec.forwardAuth.address:
|
||||
raise NeedsUpdate()
|
||||
if (
|
||||
current.spec.forwardAuth.authResponseHeadersRegex
|
||||
!= reference.spec.forwardAuth.authResponseHeadersRegex
|
||||
):
|
||||
raise NeedsUpdate()
|
||||
# Ensure all of our headers are set, others can be added by the user.
|
||||
if not set(current.spec.forwardAuth.authResponseHeaders).issubset(
|
||||
reference.spec.forwardAuth.authResponseHeaders
|
||||
):
|
||||
raise NeedsUpdate()
|
||||
|
||||
def get_reference_object(self) -> TraefikMiddleware:
|
||||
"""Get deployment object for outpost"""
|
||||
return TraefikMiddleware(
|
||||
apiVersion=f"{self.crd_group}/{self.crd_version}",
|
||||
kind="Middleware",
|
||||
metadata=TraefikMiddlewareMetadata(
|
||||
name=self.name,
|
||||
namespace=self.namespace,
|
||||
labels=self.get_object_meta().labels,
|
||||
),
|
||||
spec=TraefikMiddlewareSpec(
|
||||
forwardAuth=TraefikMiddlewareSpecForwardAuth(
|
||||
address=(
|
||||
f"http://{self.name}.{self.namespace}:9000/"
|
||||
"outpost.goauthentik.io/auth/traefik"
|
||||
),
|
||||
authResponseHeaders=[
|
||||
"X-authentik-username",
|
||||
"X-authentik-groups",
|
||||
"X-authentik-email",
|
||||
"X-authentik-name",
|
||||
"X-authentik-uid",
|
||||
"X-authentik-jwt",
|
||||
"X-authentik-meta-jwks",
|
||||
"X-authentik-meta-outpost",
|
||||
"X-authentik-meta-provider",
|
||||
"X-authentik-meta-app",
|
||||
"X-authentik-meta-version",
|
||||
],
|
||||
authResponseHeadersRegex="",
|
||||
trustForwardHeader=True,
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
def create(self, reference: TraefikMiddleware):
|
||||
return self.api.create_namespaced_custom_object(
|
||||
group=self.crd_group,
|
||||
version=self.crd_version,
|
||||
plural=self.crd_plural,
|
||||
namespace=self.namespace,
|
||||
body=asdict(reference),
|
||||
field_manager=FIELD_MANAGER,
|
||||
)
|
||||
|
||||
def delete(self, reference: TraefikMiddleware):
|
||||
return self.api.delete_namespaced_custom_object(
|
||||
group=self.crd_group,
|
||||
version=self.crd_version,
|
||||
plural=self.crd_plural,
|
||||
namespace=self.namespace,
|
||||
name=self.name,
|
||||
)
|
||||
|
||||
def retrieve(self) -> TraefikMiddleware:
|
||||
return from_dict(
|
||||
TraefikMiddleware,
|
||||
self.api.get_namespaced_custom_object(
|
||||
group=self.crd_group,
|
||||
version=self.crd_version,
|
||||
plural=self.crd_plural,
|
||||
namespace=self.namespace,
|
||||
name=self.name,
|
||||
),
|
||||
)
|
||||
|
||||
def update(self, current: TraefikMiddleware, reference: TraefikMiddleware):
|
||||
return self.api.patch_namespaced_custom_object(
|
||||
group=self.crd_group,
|
||||
version=self.crd_version,
|
||||
plural=self.crd_plural,
|
||||
namespace=self.namespace,
|
||||
name=self.name,
|
||||
body=asdict(reference),
|
||||
field_manager=FIELD_MANAGER,
|
||||
)
|
@ -90,6 +90,7 @@ class TestAuthNRequest(TestCase):
|
||||
issuer="authentik",
|
||||
pre_authentication_flow=create_test_flow(),
|
||||
signing_kp=cert,
|
||||
verification_kp=cert,
|
||||
)
|
||||
|
||||
def test_signed_valid(self):
|
||||
|
41
authentik/root/install_id.py
Normal file
41
authentik/root/install_id.py
Normal file
@ -0,0 +1,41 @@
|
||||
"""install ID"""
|
||||
from functools import lru_cache
|
||||
from uuid import uuid4
|
||||
|
||||
from psycopg2 import connect
|
||||
|
||||
from authentik.lib.config import CONFIG
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_install_id() -> str:
|
||||
"""Get install ID of this instance. The method is cached as the install ID is
|
||||
not expected to change"""
|
||||
from django.conf import settings
|
||||
from django.db import connection
|
||||
|
||||
if settings.TEST:
|
||||
return str(uuid4())
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute("SELECT id FROM authentik_install_id LIMIT 1;")
|
||||
return cursor.fetchone()[0]
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_install_id_raw():
|
||||
"""Get install_id without django loaded, this is required for the startup when we get
|
||||
the install_id but django isn't loaded yet and we can't use the function above."""
|
||||
conn = connect(
|
||||
dbname=CONFIG.y("postgresql.name"),
|
||||
user=CONFIG.y("postgresql.user"),
|
||||
password=CONFIG.y("postgresql.password"),
|
||||
host=CONFIG.y("postgresql.host"),
|
||||
port=int(CONFIG.y("postgresql.port")),
|
||||
sslmode=CONFIG.y("postgresql.sslmode"),
|
||||
sslrootcert=CONFIG.y("postgresql.sslrootcert"),
|
||||
sslcert=CONFIG.y("postgresql.sslcert"),
|
||||
sslkey=CONFIG.y("postgresql.sslkey"),
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT id FROM authentik_install_id LIMIT 1;")
|
||||
return cursor.fetchone()[0]
|
@ -1,4 +1,5 @@
|
||||
"""Dynamically set SameSite depending if the upstream connection is TLS or not"""
|
||||
from functools import lru_cache
|
||||
from hashlib import sha512
|
||||
from time import time
|
||||
from timeit import default_timer
|
||||
@ -16,10 +17,16 @@ from jwt import PyJWTError, decode, encode
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.lib.utils.http import get_client_ip
|
||||
from authentik.root.install_id import get_install_id
|
||||
|
||||
LOGGER = get_logger("authentik.asgi")
|
||||
ACR_AUTHENTIK_SESSION = "goauthentik.io/core/default"
|
||||
SIGNING_HASH = sha512(settings.SECRET_KEY.encode()).hexdigest()
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_signing_hash():
|
||||
"""Get cookie JWT signing hash"""
|
||||
return sha512(get_install_id().encode()).hexdigest()
|
||||
|
||||
|
||||
class SessionMiddleware(UpstreamSessionMiddleware):
|
||||
@ -47,7 +54,7 @@ class SessionMiddleware(UpstreamSessionMiddleware):
|
||||
# for testing setups, where the session is directly set
|
||||
session_key = key if settings.TEST else None
|
||||
try:
|
||||
session_payload = decode(key, SIGNING_HASH, algorithms=["HS256"])
|
||||
session_payload = decode(key, get_signing_hash(), algorithms=["HS256"])
|
||||
session_key = session_payload["sid"]
|
||||
except (KeyError, PyJWTError):
|
||||
pass
|
||||
@ -114,7 +121,7 @@ class SessionMiddleware(UpstreamSessionMiddleware):
|
||||
}
|
||||
if request.user.is_authenticated:
|
||||
payload["sub"] = request.user.uid
|
||||
value = encode(payload=payload, key=SIGNING_HASH)
|
||||
value = encode(payload=payload, key=get_signing_hash())
|
||||
if settings.TEST:
|
||||
value = request.session.session_key
|
||||
response.set_cookie(
|
||||
|
@ -26,6 +26,7 @@ class SAMLSourceSerializer(SourceSerializer):
|
||||
"allow_idp_initiated",
|
||||
"name_id_policy",
|
||||
"binding_type",
|
||||
"verification_kp",
|
||||
"signing_kp",
|
||||
"digest_algorithm",
|
||||
"signature_algorithm",
|
||||
@ -55,6 +56,7 @@ class SAMLSourceViewSet(UsedByMixin, ModelViewSet):
|
||||
"allow_idp_initiated",
|
||||
"name_id_policy",
|
||||
"binding_type",
|
||||
"verification_kp",
|
||||
"signing_kp",
|
||||
"digest_algorithm",
|
||||
"signature_algorithm",
|
||||
|
@ -0,0 +1,53 @@
|
||||
# Generated by Django 4.1.7 on 2023-05-19 21:55
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.apps.registry import Apps
|
||||
from django.db import migrations, models
|
||||
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||
|
||||
|
||||
def migrate_verification_cert(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||
"""Migrate signing cert to verification_kp for backwards compat"""
|
||||
|
||||
SAMLSource = apps.get_model("authentik_sources_saml", "samlsource")
|
||||
for source in SAMLSource.objects.using(schema_editor.connection.alias).all():
|
||||
source.verification_kp = source.signing_kp
|
||||
source.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("authentik_crypto", "0004_alter_certificatekeypair_name"),
|
||||
("authentik_sources_saml", "0012_usersamlsourceconnection"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="samlsource",
|
||||
name="verification_kp",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
default=None,
|
||||
help_text="When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default.",
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="+",
|
||||
to="authentik_crypto.certificatekeypair",
|
||||
verbose_name="Verification Certificate",
|
||||
),
|
||||
),
|
||||
migrations.RunPython(migrate_verification_cert),
|
||||
migrations.AlterField(
|
||||
model_name="samlsource",
|
||||
name="signing_kp",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
default=None,
|
||||
help_text="Keypair used to sign outgoing Responses going to the Identity Provider.",
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="authentik_crypto.certificatekeypair",
|
||||
verbose_name="Signing Keypair",
|
||||
),
|
||||
),
|
||||
]
|
@ -121,16 +121,27 @@ class SAMLSource(Source):
|
||||
),
|
||||
)
|
||||
|
||||
verification_kp = models.ForeignKey(
|
||||
CertificateKeyPair,
|
||||
default=None,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text=_(
|
||||
"When selected, incoming assertion's Signatures will be validated against this "
|
||||
"certificate. To allow unsigned Requests, leave on default."
|
||||
),
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name=_("Verification Certificate"),
|
||||
related_name="+",
|
||||
)
|
||||
signing_kp = models.ForeignKey(
|
||||
CertificateKeyPair,
|
||||
default=None,
|
||||
blank=True,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text=_("Keypair used to sign outgoing Responses going to the Identity Provider."),
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name=_("Signing Keypair"),
|
||||
help_text=_(
|
||||
"Keypair which is used to sign outgoing requests. Leave empty to disable signing."
|
||||
),
|
||||
on_delete=models.SET_DEFAULT,
|
||||
)
|
||||
|
||||
digest_algorithm = models.CharField(
|
||||
|
@ -72,7 +72,7 @@ class ResponseProcessor:
|
||||
self._root_xml = b64decode(raw_response.encode())
|
||||
self._root = fromstring(self._root_xml)
|
||||
|
||||
if self._source.signing_kp:
|
||||
if self._source.verification_kp:
|
||||
self._verify_signed()
|
||||
self._verify_request_id()
|
||||
self._verify_status()
|
||||
@ -89,7 +89,7 @@ class ResponseProcessor:
|
||||
|
||||
ctx = xmlsec.SignatureContext()
|
||||
key = xmlsec.Key.from_memory(
|
||||
self._source.signing_kp.certificate_data,
|
||||
self._source.verification_kp.certificate_data,
|
||||
xmlsec.constants.KeyDataFormatCertPem,
|
||||
)
|
||||
ctx.key = key
|
||||
|
@ -20,6 +20,7 @@ from authentik.flows.models import FlowDesignation, NotConfiguredAction, Stage
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||
from authentik.flows.stage import ChallengeStageView
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
from authentik.root.install_id import get_install_id
|
||||
from authentik.stages.authenticator_sms.models import SMSDevice
|
||||
from authentik.stages.authenticator_validate.challenge import (
|
||||
DeviceChallenge,
|
||||
@ -316,7 +317,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
||||
def cookie_jwt_key(self) -> str:
|
||||
"""Signing key for MFA Cookie for this stage"""
|
||||
return sha256(
|
||||
f"{settings.SECRET_KEY}:{self.executor.current_stage.pk.hex}".encode("ascii")
|
||||
f"{get_install_id()}:{self.executor.current_stage.pk.hex}".encode("ascii")
|
||||
).hexdigest()
|
||||
|
||||
def check_mfa_cookie(self, allowed_devices: list[Device]):
|
||||
|
@ -3,7 +3,6 @@ from datetime import datetime, timedelta
|
||||
from hashlib import sha256
|
||||
from time import sleep
|
||||
|
||||
from django.conf import settings
|
||||
from django.test.client import RequestFactory
|
||||
from django.urls.base import reverse
|
||||
from django_otp.oath import TOTP
|
||||
@ -17,6 +16,7 @@ from authentik.flows.stage import StageView
|
||||
from authentik.flows.tests import FlowTestCase
|
||||
from authentik.flows.views.executor import FlowExecutorView
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.root.install_id import get_install_id
|
||||
from authentik.stages.authenticator_validate.challenge import (
|
||||
get_challenge_for_device,
|
||||
validate_challenge_code,
|
||||
@ -194,7 +194,7 @@ class AuthenticatorValidateStageTOTPTests(FlowTestCase):
|
||||
"stage": stage.pk.hex + generate_id(),
|
||||
"exp": (datetime.now() + timedelta(days=3)).timestamp(),
|
||||
},
|
||||
key=sha256(f"{settings.SECRET_KEY}:{stage.pk.hex}".encode("ascii")).hexdigest(),
|
||||
key=sha256(f"{get_install_id()}:{stage.pk.hex}".encode("ascii")).hexdigest(),
|
||||
)
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
@ -233,7 +233,7 @@ class AuthenticatorValidateStageTOTPTests(FlowTestCase):
|
||||
"stage": stage.pk.hex,
|
||||
"exp": (datetime.now() + timedelta(days=3)).timestamp(),
|
||||
},
|
||||
key=sha256(f"{settings.SECRET_KEY}:{stage.pk.hex}".encode("ascii")).hexdigest(),
|
||||
key=sha256(f"{get_install_id()}:{stage.pk.hex}".encode("ascii")).hexdigest(),
|
||||
)
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
@ -272,7 +272,7 @@ class AuthenticatorValidateStageTOTPTests(FlowTestCase):
|
||||
"stage": stage.pk.hex,
|
||||
"exp": (datetime.now() - timedelta(days=3)).timestamp(),
|
||||
},
|
||||
key=sha256(f"{settings.SECRET_KEY}:{stage.pk.hex}".encode("ascii")).hexdigest(),
|
||||
key=sha256(f"{get_install_id()}:{stage.pk.hex}".encode("ascii")).hexdigest(),
|
||||
)
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
|
||||
|
@ -8,7 +8,7 @@ from authentik.flows.models import Stage
|
||||
|
||||
|
||||
class DenyStage(Stage):
|
||||
"""Cancells the current flow."""
|
||||
"""Cancels the current flow."""
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[BaseSerializer]:
|
||||
|
@ -5,10 +5,10 @@ from authentik.flows.stage import StageView
|
||||
|
||||
|
||||
class DenyStageView(StageView):
|
||||
"""Cancells the current flow"""
|
||||
"""Cancels the current flow"""
|
||||
|
||||
def get(self, request: HttpRequest) -> HttpResponse:
|
||||
"""Cancells the current flow"""
|
||||
"""Cancels the current flow"""
|
||||
return self.executor.stage_invalid()
|
||||
|
||||
def post(self, request: HttpRequest) -> HttpResponse:
|
||||
|
@ -5254,10 +5254,15 @@
|
||||
],
|
||||
"title": "Binding type"
|
||||
},
|
||||
"verification_kp": {
|
||||
"type": "integer",
|
||||
"title": "Verification Certificate",
|
||||
"description": "When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default."
|
||||
},
|
||||
"signing_kp": {
|
||||
"type": "integer",
|
||||
"title": "Signing Keypair",
|
||||
"description": "Keypair which is used to sign outgoing requests. Leave empty to disable signing."
|
||||
"description": "Keypair used to sign outgoing Responses going to the Identity Provider."
|
||||
},
|
||||
"digest_algorithm": {
|
||||
"type": "string",
|
||||
@ -8223,6 +8228,11 @@
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Path"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Password"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
|
@ -49,6 +49,9 @@ services:
|
||||
ports:
|
||||
- "${COMPOSE_PORT_HTTP:-9000}:9000"
|
||||
- "${COMPOSE_PORT_HTTPS:-9443}:9443"
|
||||
depends_on:
|
||||
- postgresql
|
||||
- redis
|
||||
worker:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.5.2}
|
||||
restart: unless-stopped
|
||||
@ -73,6 +76,9 @@ services:
|
||||
- ./custom-templates:/templates
|
||||
env_file:
|
||||
- .env
|
||||
depends_on:
|
||||
- postgresql
|
||||
- redis
|
||||
|
||||
volumes:
|
||||
database:
|
||||
|
4
go.mod
4
go.mod
@ -25,8 +25,8 @@ require (
|
||||
github.com/prometheus/client_golang v1.15.1
|
||||
github.com/sirupsen/logrus v1.9.2
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/stretchr/testify v1.8.2
|
||||
goauthentik.io/api/v3 v3.2023050.2
|
||||
github.com/stretchr/testify v1.8.3
|
||||
goauthentik.io/api/v3 v3.2023052.1
|
||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||
golang.org/x/oauth2 v0.8.0
|
||||
golang.org/x/sync v0.2.0
|
||||
|
9
go.sum
9
go.sum
@ -211,7 +211,6 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@ -219,8 +218,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
@ -241,8 +240,8 @@ go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvx
|
||||
go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
|
||||
go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
|
||||
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
|
||||
goauthentik.io/api/v3 v3.2023050.2 h1:EnwEaPM2qSFwfow0G/pTk9GHXmux0ldN77b+/gMeGTM=
|
||||
goauthentik.io/api/v3 v3.2023050.2/go.mod h1:nYECml4jGbp/541hj8GcylKQG1gVBsKppHy4+7G8u4U=
|
||||
goauthentik.io/api/v3 v3.2023052.1 h1:Sp3sBfkdBJCVTAxZte5fvBU4s0IZm6bqqli8ZawiER4=
|
||||
goauthentik.io/api/v3 v3.2023052.1/go.mod h1:nYECml4jGbp/541hj8GcylKQG1gVBsKppHy4+7G8u4U=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
|
@ -15,6 +15,7 @@ from authentik import get_full_version
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.utils.http import get_http_session
|
||||
from authentik.lib.utils.reflection import get_env
|
||||
from authentik.root.install_id import get_install_id_raw
|
||||
from lifecycle.worker import DjangoUvicornWorker
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -148,9 +149,7 @@ if not CONFIG.y_bool("disable_startup_analytics", False):
|
||||
),
|
||||
},
|
||||
headers={
|
||||
"User-Agent": sha512(str(CONFIG.y("secret_key")).encode("ascii")).hexdigest()[
|
||||
:16
|
||||
],
|
||||
"User-Agent": sha512(get_install_id_raw().encode("ascii")).hexdigest()[:16],
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
timeout=5,
|
||||
|
45
lifecycle/system_migrations/install_id.py
Normal file
45
lifecycle/system_migrations/install_id.py
Normal file
@ -0,0 +1,45 @@
|
||||
# flake8: noqa
|
||||
from uuid import uuid4
|
||||
|
||||
from authentik.lib.config import CONFIG
|
||||
from lifecycle.migrate import BaseMigration
|
||||
|
||||
SQL_STATEMENT = """BEGIN TRANSACTION;
|
||||
CREATE TABLE IF NOT EXISTS authentik_install_id (
|
||||
id TEXT NOT NULL
|
||||
);
|
||||
COMMIT;"""
|
||||
|
||||
|
||||
class Migration(BaseMigration):
|
||||
def needs_migration(self) -> bool:
|
||||
self.cur.execute(
|
||||
"select * from information_schema.tables where table_name = 'authentik_install_id';"
|
||||
)
|
||||
return not bool(self.cur.rowcount)
|
||||
|
||||
def upgrade(self, migrate=False):
|
||||
self.cur.execute(SQL_STATEMENT)
|
||||
self.con.commit()
|
||||
if migrate:
|
||||
# If we already have migrations in the database, assume we're upgrading an existing install
|
||||
# and set the install id to the secret key
|
||||
self.cur.execute(
|
||||
"INSERT INTO authentik_install_id (id) VALUES (%s)", (CONFIG.y("secret_key"),)
|
||||
)
|
||||
else:
|
||||
# Otherwise assume a new install, generate an install ID based on a UUID
|
||||
install_id = str(uuid4())
|
||||
self.cur.execute("INSERT INTO authentik_install_id (id) VALUES (%s)", (install_id,))
|
||||
self.con.commit()
|
||||
|
||||
def run(self):
|
||||
self.cur.execute(
|
||||
"select * from information_schema.tables where table_name = 'django_migrations';"
|
||||
)
|
||||
if not bool(self.cur.rowcount):
|
||||
# No django_migrations table, so generate a new id
|
||||
return self.upgrade(migrate=False)
|
||||
self.cur.execute("select count(*) from django_migrations;")
|
||||
migrations = self.cur.fetchone()[0]
|
||||
return self.upgrade(migrate=migrations > 0)
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-05-18 14:21+0000\n"
|
||||
"POT-Creation-Date: 2023-05-21 21:59+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"
|
||||
@ -31,12 +31,16 @@ msgstr ""
|
||||
msgid "Validation Error"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/blueprints/api.py:53
|
||||
#: authentik/blueprints/api.py:43
|
||||
msgid "Blueprint file does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/blueprints/api.py:54
|
||||
#, python-format
|
||||
msgid "Failed to validate blueprint: %(logs)s"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/blueprints/api.py:58
|
||||
#: authentik/blueprints/api.py:59
|
||||
msgid "Either path or content must be set."
|
||||
msgstr ""
|
||||
|
||||
@ -321,105 +325,105 @@ msgstr ""
|
||||
msgid "Certificate-Key Pairs"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:293
|
||||
#: authentik/events/models.py:290
|
||||
msgid "Event"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:294
|
||||
#: authentik/events/models.py:291
|
||||
msgid "Events"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:300
|
||||
#: authentik/events/models.py:297
|
||||
msgid "authentik inbuilt notifications"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:301
|
||||
#: authentik/events/models.py:298
|
||||
msgid "Generic Webhook"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:302
|
||||
#: authentik/events/models.py:299
|
||||
msgid "Slack Webhook (Slack/Discord)"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:303
|
||||
#: authentik/events/models.py:300
|
||||
msgid "Email"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:321
|
||||
#: authentik/events/models.py:318
|
||||
msgid ""
|
||||
"Only send notification once, for example when sending a webhook into a chat "
|
||||
"channel."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:386
|
||||
#: authentik/events/models.py:383
|
||||
msgid "Severity"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:391
|
||||
#: authentik/events/models.py:388
|
||||
msgid "Dispatched for user"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:400
|
||||
#: authentik/events/models.py:397
|
||||
msgid "Event user"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:487
|
||||
#: authentik/events/models.py:484
|
||||
msgid "Notification Transport"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:488
|
||||
#: authentik/events/models.py:485
|
||||
msgid "Notification Transports"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:494
|
||||
#: authentik/events/models.py:491
|
||||
msgid "Notice"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:495
|
||||
#: authentik/events/models.py:492
|
||||
msgid "Warning"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:496
|
||||
#: authentik/events/models.py:493
|
||||
msgid "Alert"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:521
|
||||
#: authentik/events/models.py:518
|
||||
msgid "Notification"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:522
|
||||
#: authentik/events/models.py:519
|
||||
msgid "Notifications"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:532
|
||||
#: authentik/events/models.py:529
|
||||
msgid ""
|
||||
"Select which transports should be used to notify the user. If none are "
|
||||
"selected, the notification will only be shown in the authentik UI."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:540
|
||||
#: authentik/events/models.py:537
|
||||
msgid "Controls which severity level the created notifications will have."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:545
|
||||
#: authentik/events/models.py:542
|
||||
msgid ""
|
||||
"Define which group of users this notification should be sent and shown to. "
|
||||
"If left empty, Notification won't ben sent."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:563
|
||||
#: authentik/events/models.py:560
|
||||
msgid "Notification Rule"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:564
|
||||
#: authentik/events/models.py:561
|
||||
msgid "Notification Rules"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:584
|
||||
#: authentik/events/models.py:581
|
||||
msgid "Webhook Mapping"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:585
|
||||
#: authentik/events/models.py:582
|
||||
msgid "Webhook Mappings"
|
||||
msgstr ""
|
||||
|
||||
@ -1283,49 +1287,49 @@ msgid ""
|
||||
"minutes=2;seconds=3)."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py:99 authentik/sources/saml/models.py:139
|
||||
#: authentik/providers/saml/models.py:99 authentik/sources/saml/models.py:150
|
||||
msgid "SHA1"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py:100 authentik/sources/saml/models.py:140
|
||||
#: authentik/providers/saml/models.py:100 authentik/sources/saml/models.py:151
|
||||
msgid "SHA256"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py:101 authentik/sources/saml/models.py:141
|
||||
#: authentik/providers/saml/models.py:101 authentik/sources/saml/models.py:152
|
||||
msgid "SHA384"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py:102 authentik/sources/saml/models.py:142
|
||||
#: authentik/providers/saml/models.py:102 authentik/sources/saml/models.py:153
|
||||
msgid "SHA512"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py:109 authentik/sources/saml/models.py:149
|
||||
#: authentik/providers/saml/models.py:109 authentik/sources/saml/models.py:160
|
||||
msgid "RSA-SHA1"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py:110 authentik/sources/saml/models.py:150
|
||||
#: authentik/providers/saml/models.py:110 authentik/sources/saml/models.py:161
|
||||
msgid "RSA-SHA256"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py:111 authentik/sources/saml/models.py:151
|
||||
#: authentik/providers/saml/models.py:111 authentik/sources/saml/models.py:162
|
||||
msgid "RSA-SHA384"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py:112 authentik/sources/saml/models.py:152
|
||||
#: authentik/providers/saml/models.py:112 authentik/sources/saml/models.py:163
|
||||
msgid "RSA-SHA512"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py:113 authentik/sources/saml/models.py:153
|
||||
#: authentik/providers/saml/models.py:113 authentik/sources/saml/models.py:164
|
||||
msgid "DSA-SHA1"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py:124
|
||||
#: authentik/providers/saml/models.py:124 authentik/sources/saml/models.py:130
|
||||
msgid ""
|
||||
"When selected, incoming assertion's Signatures will be validated against "
|
||||
"this certificate. To allow unsigned Requests, leave on default."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py:128
|
||||
#: authentik/providers/saml/models.py:128 authentik/sources/saml/models.py:134
|
||||
msgid "Verification Certificate"
|
||||
msgstr ""
|
||||
|
||||
@ -1333,7 +1337,7 @@ msgstr ""
|
||||
msgid "Keypair used to sign outgoing Responses going to the Service Provider."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py:138 authentik/sources/saml/models.py:129
|
||||
#: authentik/providers/saml/models.py:138 authentik/sources/saml/models.py:144
|
||||
msgid "Signing Keypair"
|
||||
msgstr ""
|
||||
|
||||
@ -1498,7 +1502,7 @@ msgstr ""
|
||||
msgid "LDAP Property Mappings"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/signals.py:56
|
||||
#: authentik/sources/ldap/signals.py:59
|
||||
msgid "Password does not match Active Directory Complexity."
|
||||
msgstr ""
|
||||
|
||||
@ -1764,25 +1768,23 @@ msgid ""
|
||||
"manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/saml/models.py:131
|
||||
msgid ""
|
||||
"Keypair which is used to sign outgoing requests. Leave empty to disable "
|
||||
"signing."
|
||||
#: authentik/sources/saml/models.py:142
|
||||
msgid "Keypair used to sign outgoing Responses going to the Identity Provider."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/saml/models.py:215
|
||||
#: authentik/sources/saml/models.py:226
|
||||
msgid "SAML Source"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/saml/models.py:216
|
||||
#: authentik/sources/saml/models.py:227
|
||||
msgid "SAML Sources"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/saml/models.py:231
|
||||
#: authentik/sources/saml/models.py:242
|
||||
msgid "User SAML Source Connection"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/saml/models.py:232
|
||||
#: authentik/sources/saml/models.py:243
|
||||
msgid "User SAML Source Connections"
|
||||
msgstr ""
|
||||
|
||||
|
@ -14,7 +14,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-05-10 17:31+0000\n"
|
||||
"POT-Creation-Date: 2023-05-21 21:59+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: deluxghost, 2023\n"
|
||||
"Language-Team: Chinese Simplified (https://app.transifex.com/authentik/teams/119923/zh-Hans/)\n"
|
||||
@ -37,12 +37,16 @@ msgstr "通用 API 错误"
|
||||
msgid "Validation Error"
|
||||
msgstr "校验错误"
|
||||
|
||||
#: authentik/blueprints/api.py:53
|
||||
#: authentik/blueprints/api.py:43
|
||||
msgid "Blueprint file does not exist"
|
||||
msgstr "蓝图文件不存在"
|
||||
|
||||
#: authentik/blueprints/api.py:54
|
||||
#, python-format
|
||||
msgid "Failed to validate blueprint: %(logs)s"
|
||||
msgstr "验证蓝图失败:%(logs)s"
|
||||
|
||||
#: authentik/blueprints/api.py:58
|
||||
#: authentik/blueprints/api.py:59
|
||||
msgid "Either path or content must be set."
|
||||
msgstr "必须设置路径或内容。"
|
||||
|
||||
@ -337,105 +341,105 @@ msgstr "证书密钥对"
|
||||
msgid "Certificate-Key Pairs"
|
||||
msgstr "证书密钥对"
|
||||
|
||||
#: authentik/events/models.py:293
|
||||
#: authentik/events/models.py:290
|
||||
msgid "Event"
|
||||
msgstr "事件"
|
||||
|
||||
#: authentik/events/models.py:294
|
||||
#: authentik/events/models.py:291
|
||||
msgid "Events"
|
||||
msgstr "事件"
|
||||
|
||||
#: authentik/events/models.py:300
|
||||
#: authentik/events/models.py:297
|
||||
msgid "authentik inbuilt notifications"
|
||||
msgstr "authentik 内置通知"
|
||||
|
||||
#: authentik/events/models.py:301
|
||||
#: authentik/events/models.py:298
|
||||
msgid "Generic Webhook"
|
||||
msgstr "通用 Webhook"
|
||||
|
||||
#: authentik/events/models.py:302
|
||||
#: authentik/events/models.py:299
|
||||
msgid "Slack Webhook (Slack/Discord)"
|
||||
msgstr "Slack Webhook(Slack/Discord)"
|
||||
|
||||
#: authentik/events/models.py:303
|
||||
#: authentik/events/models.py:300
|
||||
msgid "Email"
|
||||
msgstr "电子邮箱"
|
||||
|
||||
#: authentik/events/models.py:321
|
||||
#: authentik/events/models.py:318
|
||||
msgid ""
|
||||
"Only send notification once, for example when sending a webhook into a chat "
|
||||
"channel."
|
||||
msgstr "仅发送一次通知,例如在向聊天频道发送 Webhook 时。"
|
||||
|
||||
#: authentik/events/models.py:386
|
||||
#: authentik/events/models.py:383
|
||||
msgid "Severity"
|
||||
msgstr "严重程度"
|
||||
|
||||
#: authentik/events/models.py:391
|
||||
#: authentik/events/models.py:388
|
||||
msgid "Dispatched for user"
|
||||
msgstr "为用户分派"
|
||||
|
||||
#: authentik/events/models.py:400
|
||||
#: authentik/events/models.py:397
|
||||
msgid "Event user"
|
||||
msgstr "事件用户"
|
||||
|
||||
#: authentik/events/models.py:487
|
||||
#: authentik/events/models.py:484
|
||||
msgid "Notification Transport"
|
||||
msgstr "通知传输"
|
||||
|
||||
#: authentik/events/models.py:488
|
||||
#: authentik/events/models.py:485
|
||||
msgid "Notification Transports"
|
||||
msgstr "通知传输"
|
||||
|
||||
#: authentik/events/models.py:494
|
||||
#: authentik/events/models.py:491
|
||||
msgid "Notice"
|
||||
msgstr "通知"
|
||||
|
||||
#: authentik/events/models.py:495
|
||||
#: authentik/events/models.py:492
|
||||
msgid "Warning"
|
||||
msgstr "警告"
|
||||
|
||||
#: authentik/events/models.py:496
|
||||
#: authentik/events/models.py:493
|
||||
msgid "Alert"
|
||||
msgstr "注意"
|
||||
|
||||
#: authentik/events/models.py:521
|
||||
#: authentik/events/models.py:518
|
||||
msgid "Notification"
|
||||
msgstr "通知"
|
||||
|
||||
#: authentik/events/models.py:522
|
||||
#: authentik/events/models.py:519
|
||||
msgid "Notifications"
|
||||
msgstr "通知"
|
||||
|
||||
#: authentik/events/models.py:532
|
||||
#: authentik/events/models.py:529
|
||||
msgid ""
|
||||
"Select which transports should be used to notify the user. If none are "
|
||||
"selected, the notification will only be shown in the authentik UI."
|
||||
msgstr "选择应使用哪些传输方式来通知用户。如果未选择任何内容,则通知将仅显示在 authentik UI 中。"
|
||||
|
||||
#: authentik/events/models.py:540
|
||||
#: authentik/events/models.py:537
|
||||
msgid "Controls which severity level the created notifications will have."
|
||||
msgstr "控制被创建的通知的严重性级别。"
|
||||
|
||||
#: authentik/events/models.py:545
|
||||
#: authentik/events/models.py:542
|
||||
msgid ""
|
||||
"Define which group of users this notification should be sent and shown to. "
|
||||
"If left empty, Notification won't ben sent."
|
||||
msgstr "定义此通知应该发送到哪些用户组。如果留空,则不会发送通知。"
|
||||
|
||||
#: authentik/events/models.py:563
|
||||
#: authentik/events/models.py:560
|
||||
msgid "Notification Rule"
|
||||
msgstr "通知规则"
|
||||
|
||||
#: authentik/events/models.py:564
|
||||
#: authentik/events/models.py:561
|
||||
msgid "Notification Rules"
|
||||
msgstr "通知规则"
|
||||
|
||||
#: authentik/events/models.py:584
|
||||
#: authentik/events/models.py:581
|
||||
msgid "Webhook Mapping"
|
||||
msgstr "Webhook 映射"
|
||||
|
||||
#: authentik/events/models.py:585
|
||||
#: authentik/events/models.py:582
|
||||
msgid "Webhook Mappings"
|
||||
msgstr "Webhook 映射"
|
||||
|
||||
@ -1312,49 +1316,49 @@ msgid ""
|
||||
"hours=1;minutes=2;seconds=3)."
|
||||
msgstr "从当前时间经过多久时或之后,会话无效(格式:hours=1;minutes=2;seconds=3)。"
|
||||
|
||||
#: authentik/providers/saml/models.py:99 authentik/sources/saml/models.py:139
|
||||
#: authentik/providers/saml/models.py:99 authentik/sources/saml/models.py:150
|
||||
msgid "SHA1"
|
||||
msgstr "SHA1"
|
||||
|
||||
#: authentik/providers/saml/models.py:100 authentik/sources/saml/models.py:140
|
||||
#: authentik/providers/saml/models.py:100 authentik/sources/saml/models.py:151
|
||||
msgid "SHA256"
|
||||
msgstr "SHA256"
|
||||
|
||||
#: authentik/providers/saml/models.py:101 authentik/sources/saml/models.py:141
|
||||
#: authentik/providers/saml/models.py:101 authentik/sources/saml/models.py:152
|
||||
msgid "SHA384"
|
||||
msgstr "SHA384"
|
||||
|
||||
#: authentik/providers/saml/models.py:102 authentik/sources/saml/models.py:142
|
||||
#: authentik/providers/saml/models.py:102 authentik/sources/saml/models.py:153
|
||||
msgid "SHA512"
|
||||
msgstr "SHA512"
|
||||
|
||||
#: authentik/providers/saml/models.py:109 authentik/sources/saml/models.py:149
|
||||
#: authentik/providers/saml/models.py:109 authentik/sources/saml/models.py:160
|
||||
msgid "RSA-SHA1"
|
||||
msgstr "RSA-SHA1"
|
||||
|
||||
#: authentik/providers/saml/models.py:110 authentik/sources/saml/models.py:150
|
||||
#: authentik/providers/saml/models.py:110 authentik/sources/saml/models.py:161
|
||||
msgid "RSA-SHA256"
|
||||
msgstr "RSA-SHA256"
|
||||
|
||||
#: authentik/providers/saml/models.py:111 authentik/sources/saml/models.py:151
|
||||
#: authentik/providers/saml/models.py:111 authentik/sources/saml/models.py:162
|
||||
msgid "RSA-SHA384"
|
||||
msgstr "RSA-SHA384"
|
||||
|
||||
#: authentik/providers/saml/models.py:112 authentik/sources/saml/models.py:152
|
||||
#: authentik/providers/saml/models.py:112 authentik/sources/saml/models.py:163
|
||||
msgid "RSA-SHA512"
|
||||
msgstr "RSA-SHA512"
|
||||
|
||||
#: authentik/providers/saml/models.py:113 authentik/sources/saml/models.py:153
|
||||
#: authentik/providers/saml/models.py:113 authentik/sources/saml/models.py:164
|
||||
msgid "DSA-SHA1"
|
||||
msgstr "DSA-SHA1"
|
||||
|
||||
#: authentik/providers/saml/models.py:124
|
||||
#: authentik/providers/saml/models.py:124 authentik/sources/saml/models.py:130
|
||||
msgid ""
|
||||
"When selected, incoming assertion's Signatures will be validated against "
|
||||
"this certificate. To allow unsigned Requests, leave on default."
|
||||
msgstr "选中后,传入断言的签名将根据此证书进行验证。要允许未签名的请求,请保留默认值。"
|
||||
|
||||
#: authentik/providers/saml/models.py:128
|
||||
#: authentik/providers/saml/models.py:128 authentik/sources/saml/models.py:134
|
||||
msgid "Verification Certificate"
|
||||
msgstr "验证证书"
|
||||
|
||||
@ -1362,7 +1366,7 @@ msgstr "验证证书"
|
||||
msgid "Keypair used to sign outgoing Responses going to the Service Provider."
|
||||
msgstr "密钥对,用于签署发送给服务提供程序的传出响应。"
|
||||
|
||||
#: authentik/providers/saml/models.py:138 authentik/sources/saml/models.py:129
|
||||
#: authentik/providers/saml/models.py:138 authentik/sources/saml/models.py:144
|
||||
msgid "Signing Keypair"
|
||||
msgstr "签名密钥对"
|
||||
|
||||
@ -1410,34 +1414,34 @@ msgstr "SCIM 映射"
|
||||
msgid "SCIM Mappings"
|
||||
msgstr "SCIM 映射"
|
||||
|
||||
#: authentik/providers/scim/tasks.py:50
|
||||
#: authentik/providers/scim/tasks.py:52
|
||||
msgid "Starting full SCIM sync"
|
||||
msgstr "开始全量 SCIM 同步"
|
||||
|
||||
#: authentik/providers/scim/tasks.py:57
|
||||
#: authentik/providers/scim/tasks.py:59
|
||||
#, python-format
|
||||
msgid "Syncing page %(page)d of users"
|
||||
msgstr "正在同步用户页面 %(page)d"
|
||||
|
||||
#: authentik/providers/scim/tasks.py:61
|
||||
#: authentik/providers/scim/tasks.py:63
|
||||
#, python-format
|
||||
msgid "Syncing page %(page)d of groups"
|
||||
msgstr "正在同步群组页面 %(page)d"
|
||||
|
||||
#: authentik/providers/scim/tasks.py:90
|
||||
#: authentik/providers/scim/tasks.py:92
|
||||
#, python-format
|
||||
msgid "Failed to sync user due to remote error %(name)s: %(error)s"
|
||||
msgstr "由于远端错误 %(name)s,同步用户失败:%(error)s"
|
||||
msgid "Failed to sync user %(user_name)s due to remote error: %(error)s"
|
||||
msgstr "由于远端错误,同步用户 %(user_name)s 失败:%(error)s"
|
||||
|
||||
#: authentik/providers/scim/tasks.py:101 authentik/providers/scim/tasks.py:142
|
||||
#: authentik/providers/scim/tasks.py:103 authentik/providers/scim/tasks.py:144
|
||||
#, python-format
|
||||
msgid "Stopping sync due to error: %(error)s"
|
||||
msgstr "由于以下错误,同步停止:%(error)s"
|
||||
|
||||
#: authentik/providers/scim/tasks.py:131
|
||||
#: authentik/providers/scim/tasks.py:133
|
||||
#, python-format
|
||||
msgid "Failed to sync group due to remote error %(name)s: %(error)s"
|
||||
msgstr "由于远端错误 %(name)s,同步群组失败:%(error)s"
|
||||
msgid "Failed to sync group %(group_name)s due to remote error: %(error)s"
|
||||
msgstr "由于远端错误,同步组 %(group_name)s 失败:%(error)s"
|
||||
|
||||
#: authentik/recovery/management/commands/create_admin_group.py:11
|
||||
msgid "Create admin group if the default group gets deleted."
|
||||
@ -1527,7 +1531,7 @@ msgstr "LDAP 属性映射"
|
||||
msgid "LDAP Property Mappings"
|
||||
msgstr "LDAP 属性映射"
|
||||
|
||||
#: authentik/sources/ldap/signals.py:56
|
||||
#: authentik/sources/ldap/signals.py:59
|
||||
msgid "Password does not match Active Directory Complexity."
|
||||
msgstr "密码与 Active Directory 复杂度不匹配。"
|
||||
|
||||
@ -1797,25 +1801,24 @@ msgstr ""
|
||||
"删除临时用户的时间偏移。这仅适用于您的 IDP 使用 NameID 格式 'transient' "
|
||||
"且用户未手动登出的情况。(格式:hours=1;minutes=2;seconds=3)。"
|
||||
|
||||
#: authentik/sources/saml/models.py:131
|
||||
#: authentik/sources/saml/models.py:142
|
||||
msgid ""
|
||||
"Keypair which is used to sign outgoing requests. Leave empty to disable "
|
||||
"signing."
|
||||
msgstr "用于签署传出请求的密钥对。留空则禁用签名。"
|
||||
"Keypair used to sign outgoing Responses going to the Identity Provider."
|
||||
msgstr "密钥对,用于签署发送给身份提供程序的传出响应。"
|
||||
|
||||
#: authentik/sources/saml/models.py:215
|
||||
#: authentik/sources/saml/models.py:226
|
||||
msgid "SAML Source"
|
||||
msgstr "SAML 源"
|
||||
|
||||
#: authentik/sources/saml/models.py:216
|
||||
#: authentik/sources/saml/models.py:227
|
||||
msgid "SAML Sources"
|
||||
msgstr "SAML 源"
|
||||
|
||||
#: authentik/sources/saml/models.py:231
|
||||
#: authentik/sources/saml/models.py:242
|
||||
msgid "User SAML Source Connection"
|
||||
msgstr "用户 SAML 源连接"
|
||||
|
||||
#: authentik/sources/saml/models.py:232
|
||||
#: authentik/sources/saml/models.py:243
|
||||
msgid "User SAML Source Connections"
|
||||
msgstr "用户 SAML 源连接"
|
||||
|
||||
@ -2150,6 +2153,10 @@ msgstr ""
|
||||
" 这是一封测试电子邮件,用于通知您已成功配置 authentik 电子邮件。\n"
|
||||
" "
|
||||
|
||||
#: authentik/stages/identification/api.py:20
|
||||
msgid "When no user fields are selected, at least one source must be selected"
|
||||
msgstr "如果未选择用户字段,则至少要选择一个源"
|
||||
|
||||
#: authentik/stages/identification/models.py:29
|
||||
msgid ""
|
||||
"Fields of the user object to match against. (Hold shift to select multiple "
|
||||
@ -2443,17 +2450,18 @@ msgstr "用户写入阶段"
|
||||
msgid "User Write Stages"
|
||||
msgstr "用户写入阶段"
|
||||
|
||||
#: authentik/stages/user_write/stage.py:132
|
||||
#: authentik/stages/user_write/stage.py:133
|
||||
msgid "No Pending data."
|
||||
msgstr "没有待处理的数据。"
|
||||
|
||||
#: authentik/stages/user_write/stage.py:138
|
||||
#: authentik/stages/user_write/stage.py:139
|
||||
msgid "No user found and can't create new user."
|
||||
msgstr "未找到用户并且无法创建新用户。"
|
||||
|
||||
#: authentik/stages/user_write/stage.py:165
|
||||
msgid "Failed to save user"
|
||||
msgstr "保存用户失败"
|
||||
#: authentik/stages/user_write/stage.py:156
|
||||
#: authentik/stages/user_write/stage.py:170
|
||||
msgid "Failed to update user. Please try again later."
|
||||
msgstr "更新用户失败。请稍后重试。"
|
||||
|
||||
#: authentik/tenants/models.py:23
|
||||
msgid ""
|
||||
|
@ -14,7 +14,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-05-10 17:31+0000\n"
|
||||
"POT-Creation-Date: 2023-05-21 21:59+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: deluxghost, 2023\n"
|
||||
"Language-Team: Chinese (China) (https://app.transifex.com/authentik/teams/119923/zh_CN/)\n"
|
||||
@ -37,12 +37,16 @@ msgstr "通用 API 错误"
|
||||
msgid "Validation Error"
|
||||
msgstr "校验错误"
|
||||
|
||||
#: authentik/blueprints/api.py:53
|
||||
#: authentik/blueprints/api.py:43
|
||||
msgid "Blueprint file does not exist"
|
||||
msgstr "蓝图文件不存在"
|
||||
|
||||
#: authentik/blueprints/api.py:54
|
||||
#, python-format
|
||||
msgid "Failed to validate blueprint: %(logs)s"
|
||||
msgstr "验证蓝图失败:%(logs)s"
|
||||
|
||||
#: authentik/blueprints/api.py:58
|
||||
#: authentik/blueprints/api.py:59
|
||||
msgid "Either path or content must be set."
|
||||
msgstr "必须设置路径或内容。"
|
||||
|
||||
@ -337,105 +341,105 @@ msgstr "证书密钥对"
|
||||
msgid "Certificate-Key Pairs"
|
||||
msgstr "证书密钥对"
|
||||
|
||||
#: authentik/events/models.py:293
|
||||
#: authentik/events/models.py:290
|
||||
msgid "Event"
|
||||
msgstr "事件"
|
||||
|
||||
#: authentik/events/models.py:294
|
||||
#: authentik/events/models.py:291
|
||||
msgid "Events"
|
||||
msgstr "事件"
|
||||
|
||||
#: authentik/events/models.py:300
|
||||
#: authentik/events/models.py:297
|
||||
msgid "authentik inbuilt notifications"
|
||||
msgstr "authentik 内置通知"
|
||||
|
||||
#: authentik/events/models.py:301
|
||||
#: authentik/events/models.py:298
|
||||
msgid "Generic Webhook"
|
||||
msgstr "通用 Webhook"
|
||||
|
||||
#: authentik/events/models.py:302
|
||||
#: authentik/events/models.py:299
|
||||
msgid "Slack Webhook (Slack/Discord)"
|
||||
msgstr "Slack Webhook(Slack/Discord)"
|
||||
|
||||
#: authentik/events/models.py:303
|
||||
#: authentik/events/models.py:300
|
||||
msgid "Email"
|
||||
msgstr "电子邮箱"
|
||||
|
||||
#: authentik/events/models.py:321
|
||||
#: authentik/events/models.py:318
|
||||
msgid ""
|
||||
"Only send notification once, for example when sending a webhook into a chat "
|
||||
"channel."
|
||||
msgstr "仅发送一次通知,例如在向聊天频道发送 Webhook 时。"
|
||||
|
||||
#: authentik/events/models.py:386
|
||||
#: authentik/events/models.py:383
|
||||
msgid "Severity"
|
||||
msgstr "严重程度"
|
||||
|
||||
#: authentik/events/models.py:391
|
||||
#: authentik/events/models.py:388
|
||||
msgid "Dispatched for user"
|
||||
msgstr "为用户分派"
|
||||
|
||||
#: authentik/events/models.py:400
|
||||
#: authentik/events/models.py:397
|
||||
msgid "Event user"
|
||||
msgstr "事件用户"
|
||||
|
||||
#: authentik/events/models.py:487
|
||||
#: authentik/events/models.py:484
|
||||
msgid "Notification Transport"
|
||||
msgstr "通知传输"
|
||||
|
||||
#: authentik/events/models.py:488
|
||||
#: authentik/events/models.py:485
|
||||
msgid "Notification Transports"
|
||||
msgstr "通知传输"
|
||||
|
||||
#: authentik/events/models.py:494
|
||||
#: authentik/events/models.py:491
|
||||
msgid "Notice"
|
||||
msgstr "通知"
|
||||
|
||||
#: authentik/events/models.py:495
|
||||
#: authentik/events/models.py:492
|
||||
msgid "Warning"
|
||||
msgstr "警告"
|
||||
|
||||
#: authentik/events/models.py:496
|
||||
#: authentik/events/models.py:493
|
||||
msgid "Alert"
|
||||
msgstr "注意"
|
||||
|
||||
#: authentik/events/models.py:521
|
||||
#: authentik/events/models.py:518
|
||||
msgid "Notification"
|
||||
msgstr "通知"
|
||||
|
||||
#: authentik/events/models.py:522
|
||||
#: authentik/events/models.py:519
|
||||
msgid "Notifications"
|
||||
msgstr "通知"
|
||||
|
||||
#: authentik/events/models.py:532
|
||||
#: authentik/events/models.py:529
|
||||
msgid ""
|
||||
"Select which transports should be used to notify the user. If none are "
|
||||
"selected, the notification will only be shown in the authentik UI."
|
||||
msgstr "选择应使用哪些传输方式来通知用户。如果未选择任何内容,则通知将仅显示在 authentik UI 中。"
|
||||
|
||||
#: authentik/events/models.py:540
|
||||
#: authentik/events/models.py:537
|
||||
msgid "Controls which severity level the created notifications will have."
|
||||
msgstr "控制被创建的通知的严重性级别。"
|
||||
|
||||
#: authentik/events/models.py:545
|
||||
#: authentik/events/models.py:542
|
||||
msgid ""
|
||||
"Define which group of users this notification should be sent and shown to. "
|
||||
"If left empty, Notification won't ben sent."
|
||||
msgstr "定义此通知应该发送到哪些用户组。如果留空,则不会发送通知。"
|
||||
|
||||
#: authentik/events/models.py:563
|
||||
#: authentik/events/models.py:560
|
||||
msgid "Notification Rule"
|
||||
msgstr "通知规则"
|
||||
|
||||
#: authentik/events/models.py:564
|
||||
#: authentik/events/models.py:561
|
||||
msgid "Notification Rules"
|
||||
msgstr "通知规则"
|
||||
|
||||
#: authentik/events/models.py:584
|
||||
#: authentik/events/models.py:581
|
||||
msgid "Webhook Mapping"
|
||||
msgstr "Webhook 映射"
|
||||
|
||||
#: authentik/events/models.py:585
|
||||
#: authentik/events/models.py:582
|
||||
msgid "Webhook Mappings"
|
||||
msgstr "Webhook 映射"
|
||||
|
||||
@ -1312,49 +1316,49 @@ msgid ""
|
||||
"hours=1;minutes=2;seconds=3)."
|
||||
msgstr "从当前时间经过多久时或之后,会话无效(格式:hours=1;minutes=2;seconds=3)。"
|
||||
|
||||
#: authentik/providers/saml/models.py:99 authentik/sources/saml/models.py:139
|
||||
#: authentik/providers/saml/models.py:99 authentik/sources/saml/models.py:150
|
||||
msgid "SHA1"
|
||||
msgstr "SHA1"
|
||||
|
||||
#: authentik/providers/saml/models.py:100 authentik/sources/saml/models.py:140
|
||||
#: authentik/providers/saml/models.py:100 authentik/sources/saml/models.py:151
|
||||
msgid "SHA256"
|
||||
msgstr "SHA256"
|
||||
|
||||
#: authentik/providers/saml/models.py:101 authentik/sources/saml/models.py:141
|
||||
#: authentik/providers/saml/models.py:101 authentik/sources/saml/models.py:152
|
||||
msgid "SHA384"
|
||||
msgstr "SHA384"
|
||||
|
||||
#: authentik/providers/saml/models.py:102 authentik/sources/saml/models.py:142
|
||||
#: authentik/providers/saml/models.py:102 authentik/sources/saml/models.py:153
|
||||
msgid "SHA512"
|
||||
msgstr "SHA512"
|
||||
|
||||
#: authentik/providers/saml/models.py:109 authentik/sources/saml/models.py:149
|
||||
#: authentik/providers/saml/models.py:109 authentik/sources/saml/models.py:160
|
||||
msgid "RSA-SHA1"
|
||||
msgstr "RSA-SHA1"
|
||||
|
||||
#: authentik/providers/saml/models.py:110 authentik/sources/saml/models.py:150
|
||||
#: authentik/providers/saml/models.py:110 authentik/sources/saml/models.py:161
|
||||
msgid "RSA-SHA256"
|
||||
msgstr "RSA-SHA256"
|
||||
|
||||
#: authentik/providers/saml/models.py:111 authentik/sources/saml/models.py:151
|
||||
#: authentik/providers/saml/models.py:111 authentik/sources/saml/models.py:162
|
||||
msgid "RSA-SHA384"
|
||||
msgstr "RSA-SHA384"
|
||||
|
||||
#: authentik/providers/saml/models.py:112 authentik/sources/saml/models.py:152
|
||||
#: authentik/providers/saml/models.py:112 authentik/sources/saml/models.py:163
|
||||
msgid "RSA-SHA512"
|
||||
msgstr "RSA-SHA512"
|
||||
|
||||
#: authentik/providers/saml/models.py:113 authentik/sources/saml/models.py:153
|
||||
#: authentik/providers/saml/models.py:113 authentik/sources/saml/models.py:164
|
||||
msgid "DSA-SHA1"
|
||||
msgstr "DSA-SHA1"
|
||||
|
||||
#: authentik/providers/saml/models.py:124
|
||||
#: authentik/providers/saml/models.py:124 authentik/sources/saml/models.py:130
|
||||
msgid ""
|
||||
"When selected, incoming assertion's Signatures will be validated against "
|
||||
"this certificate. To allow unsigned Requests, leave on default."
|
||||
msgstr "选中后,传入断言的签名将根据此证书进行验证。要允许未签名的请求,请保留默认值。"
|
||||
|
||||
#: authentik/providers/saml/models.py:128
|
||||
#: authentik/providers/saml/models.py:128 authentik/sources/saml/models.py:134
|
||||
msgid "Verification Certificate"
|
||||
msgstr "验证证书"
|
||||
|
||||
@ -1362,7 +1366,7 @@ msgstr "验证证书"
|
||||
msgid "Keypair used to sign outgoing Responses going to the Service Provider."
|
||||
msgstr "密钥对,用于签署发送给服务提供程序的传出响应。"
|
||||
|
||||
#: authentik/providers/saml/models.py:138 authentik/sources/saml/models.py:129
|
||||
#: authentik/providers/saml/models.py:138 authentik/sources/saml/models.py:144
|
||||
msgid "Signing Keypair"
|
||||
msgstr "签名密钥对"
|
||||
|
||||
@ -1410,34 +1414,34 @@ msgstr "SCIM 映射"
|
||||
msgid "SCIM Mappings"
|
||||
msgstr "SCIM 映射"
|
||||
|
||||
#: authentik/providers/scim/tasks.py:50
|
||||
#: authentik/providers/scim/tasks.py:52
|
||||
msgid "Starting full SCIM sync"
|
||||
msgstr "开始全量 SCIM 同步"
|
||||
|
||||
#: authentik/providers/scim/tasks.py:57
|
||||
#: authentik/providers/scim/tasks.py:59
|
||||
#, python-format
|
||||
msgid "Syncing page %(page)d of users"
|
||||
msgstr "正在同步用户页面 %(page)d"
|
||||
|
||||
#: authentik/providers/scim/tasks.py:61
|
||||
#: authentik/providers/scim/tasks.py:63
|
||||
#, python-format
|
||||
msgid "Syncing page %(page)d of groups"
|
||||
msgstr "正在同步群组页面 %(page)d"
|
||||
|
||||
#: authentik/providers/scim/tasks.py:90
|
||||
#: authentik/providers/scim/tasks.py:92
|
||||
#, python-format
|
||||
msgid "Failed to sync user due to remote error %(name)s: %(error)s"
|
||||
msgstr "由于远端错误 %(name)s,同步用户失败:%(error)s"
|
||||
msgid "Failed to sync user %(user_name)s due to remote error: %(error)s"
|
||||
msgstr "由于远端错误,同步用户 %(user_name)s 失败:%(error)s"
|
||||
|
||||
#: authentik/providers/scim/tasks.py:101 authentik/providers/scim/tasks.py:142
|
||||
#: authentik/providers/scim/tasks.py:103 authentik/providers/scim/tasks.py:144
|
||||
#, python-format
|
||||
msgid "Stopping sync due to error: %(error)s"
|
||||
msgstr "由于以下错误,同步停止:%(error)s"
|
||||
|
||||
#: authentik/providers/scim/tasks.py:131
|
||||
#: authentik/providers/scim/tasks.py:133
|
||||
#, python-format
|
||||
msgid "Failed to sync group due to remote error %(name)s: %(error)s"
|
||||
msgstr "由于远端错误 %(name)s,同步群组失败:%(error)s"
|
||||
msgid "Failed to sync group %(group_name)s due to remote error: %(error)s"
|
||||
msgstr "由于远端错误,同步组 %(group_name)s 失败:%(error)s"
|
||||
|
||||
#: authentik/recovery/management/commands/create_admin_group.py:11
|
||||
msgid "Create admin group if the default group gets deleted."
|
||||
@ -1527,7 +1531,7 @@ msgstr "LDAP 属性映射"
|
||||
msgid "LDAP Property Mappings"
|
||||
msgstr "LDAP 属性映射"
|
||||
|
||||
#: authentik/sources/ldap/signals.py:56
|
||||
#: authentik/sources/ldap/signals.py:59
|
||||
msgid "Password does not match Active Directory Complexity."
|
||||
msgstr "密码与 Active Directory 复杂度不匹配。"
|
||||
|
||||
@ -1797,25 +1801,24 @@ msgstr ""
|
||||
"删除临时用户的时间偏移。这仅适用于您的 IDP 使用 NameID 格式 'transient' "
|
||||
"且用户未手动登出的情况。(格式:hours=1;minutes=2;seconds=3)。"
|
||||
|
||||
#: authentik/sources/saml/models.py:131
|
||||
#: authentik/sources/saml/models.py:142
|
||||
msgid ""
|
||||
"Keypair which is used to sign outgoing requests. Leave empty to disable "
|
||||
"signing."
|
||||
msgstr "用于签署传出请求的密钥对。留空则禁用签名。"
|
||||
"Keypair used to sign outgoing Responses going to the Identity Provider."
|
||||
msgstr "密钥对,用于签署发送给身份提供程序的传出响应。"
|
||||
|
||||
#: authentik/sources/saml/models.py:215
|
||||
#: authentik/sources/saml/models.py:226
|
||||
msgid "SAML Source"
|
||||
msgstr "SAML 源"
|
||||
|
||||
#: authentik/sources/saml/models.py:216
|
||||
#: authentik/sources/saml/models.py:227
|
||||
msgid "SAML Sources"
|
||||
msgstr "SAML 源"
|
||||
|
||||
#: authentik/sources/saml/models.py:231
|
||||
#: authentik/sources/saml/models.py:242
|
||||
msgid "User SAML Source Connection"
|
||||
msgstr "用户 SAML 源连接"
|
||||
|
||||
#: authentik/sources/saml/models.py:232
|
||||
#: authentik/sources/saml/models.py:243
|
||||
msgid "User SAML Source Connections"
|
||||
msgstr "用户 SAML 源连接"
|
||||
|
||||
@ -2150,6 +2153,10 @@ msgstr ""
|
||||
" 这是一封测试电子邮件,用于通知您已成功配置 authentik 电子邮件。\n"
|
||||
" "
|
||||
|
||||
#: authentik/stages/identification/api.py:20
|
||||
msgid "When no user fields are selected, at least one source must be selected"
|
||||
msgstr "如果未选择用户字段,则至少要选择一个源"
|
||||
|
||||
#: authentik/stages/identification/models.py:29
|
||||
msgid ""
|
||||
"Fields of the user object to match against. (Hold shift to select multiple "
|
||||
@ -2443,17 +2450,18 @@ msgstr "用户写入阶段"
|
||||
msgid "User Write Stages"
|
||||
msgstr "用户写入阶段"
|
||||
|
||||
#: authentik/stages/user_write/stage.py:132
|
||||
#: authentik/stages/user_write/stage.py:133
|
||||
msgid "No Pending data."
|
||||
msgstr "没有待处理的数据。"
|
||||
|
||||
#: authentik/stages/user_write/stage.py:138
|
||||
#: authentik/stages/user_write/stage.py:139
|
||||
msgid "No user found and can't create new user."
|
||||
msgstr "未找到用户并且无法创建新用户。"
|
||||
|
||||
#: authentik/stages/user_write/stage.py:165
|
||||
msgid "Failed to save user"
|
||||
msgstr "保存用户失败"
|
||||
#: authentik/stages/user_write/stage.py:156
|
||||
#: authentik/stages/user_write/stage.py:170
|
||||
msgid "Failed to update user. Please try again later."
|
||||
msgstr "更新用户失败。请稍后重试。"
|
||||
|
||||
#: authentik/tenants/models.py:23
|
||||
msgid ""
|
||||
|
284
poetry.lock
generated
284
poetry.lock
generated
@ -878,63 +878,72 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.2.6"
|
||||
version = "7.2.7"
|
||||
description = "Code coverage measurement for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "coverage-7.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:496b86f1fc9c81a1cd53d8842ef712e950a4611bba0c42d33366a7b91ba969ec"},
|
||||
{file = "coverage-7.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fbe6e8c0a9a7193ba10ee52977d4d5e7652957c1f56ccefed0701db8801a2a3b"},
|
||||
{file = "coverage-7.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d06b721c2550c01a60e5d3093f417168658fb454e5dfd9a23570e9bffe39a1"},
|
||||
{file = "coverage-7.2.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:77a04b84d01f0e12c66f16e69e92616442dc675bbe51b90bfb074b1e5d1c7fbd"},
|
||||
{file = "coverage-7.2.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35db06450272473eab4449e9c2ad9bc6a0a68dab8e81a0eae6b50d9c2838767e"},
|
||||
{file = "coverage-7.2.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6727a0d929ff0028b1ed8b3e7f8701670b1d7032f219110b55476bb60c390bfb"},
|
||||
{file = "coverage-7.2.6-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aac1d5fdc5378f6bac2c0c7ebe7635a6809f5b4376f6cf5d43243c1917a67087"},
|
||||
{file = "coverage-7.2.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1c9e4a5eb1bbc3675ee57bc31f8eea4cd7fb0cbcbe4912cf1cb2bf3b754f4a80"},
|
||||
{file = "coverage-7.2.6-cp310-cp310-win32.whl", hash = "sha256:71f739f97f5f80627f1fee2331e63261355fd1e9a9cce0016394b6707ac3f4ec"},
|
||||
{file = "coverage-7.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:fde5c7a9d9864d3e07992f66767a9817f24324f354caa3d8129735a3dc74f126"},
|
||||
{file = "coverage-7.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bc7b667f8654376e9353dd93e55e12ce2a59fb6d8e29fce40de682273425e044"},
|
||||
{file = "coverage-7.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:697f4742aa3f26c107ddcb2b1784a74fe40180014edbd9adaa574eac0529914c"},
|
||||
{file = "coverage-7.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:541280dde49ce74a4262c5e395b48ea1207e78454788887118c421cb4ffbfcac"},
|
||||
{file = "coverage-7.2.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e7f1a8328eeec34c54f1d5968a708b50fc38d31e62ca8b0560e84a968fbf9a9"},
|
||||
{file = "coverage-7.2.6-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bbd58eb5a2371bf160590f4262109f66b6043b0b991930693134cb617bc0169"},
|
||||
{file = "coverage-7.2.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ae82c5f168d2a39a5d69a12a69d4dc23837a43cf2ca99be60dfe59996ea6b113"},
|
||||
{file = "coverage-7.2.6-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f5440cdaf3099e7ab17a5a7065aed59aff8c8b079597b61c1f8be6f32fe60636"},
|
||||
{file = "coverage-7.2.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a6f03f87fea579d55e0b690d28f5042ec1368650466520fbc400e7aeaf09e995"},
|
||||
{file = "coverage-7.2.6-cp311-cp311-win32.whl", hash = "sha256:dc4d5187ef4d53e0d4c8eaf530233685667844c5fb0b855fea71ae659017854b"},
|
||||
{file = "coverage-7.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:c93d52c3dc7b9c65e39473704988602300e3cc1bad08b5ab5b03ca98bbbc68c1"},
|
||||
{file = "coverage-7.2.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:42c692b55a647a832025a4c048007034fe77b162b566ad537ce65ad824b12a84"},
|
||||
{file = "coverage-7.2.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7786b2fa7809bf835f830779ad285215a04da76293164bb6745796873f0942d"},
|
||||
{file = "coverage-7.2.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25bad4196104761bc26b1dae9b57383826542ec689ff0042f7f4f4dd7a815cba"},
|
||||
{file = "coverage-7.2.6-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2692306d3d4cb32d2cceed1e47cebd6b1d2565c993d6d2eda8e6e6adf53301e6"},
|
||||
{file = "coverage-7.2.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:392154d09bd4473b9d11351ab5d63391f3d5d24d752f27b3be7498b0ee2b5226"},
|
||||
{file = "coverage-7.2.6-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fa079995432037b5e2ef5ddbb270bcd2ded9f52b8e191a5de11fe59a00ea30d8"},
|
||||
{file = "coverage-7.2.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d712cefff15c712329113b01088ba71bbcef0f7ea58478ca0bbec63a824844cb"},
|
||||
{file = "coverage-7.2.6-cp37-cp37m-win32.whl", hash = "sha256:004948e296149644d208964300cb3d98affc5211e9e490e9979af4030b0d6473"},
|
||||
{file = "coverage-7.2.6-cp37-cp37m-win_amd64.whl", hash = "sha256:c1d7a31603c3483ac49c1726723b0934f88f2c011c660e6471e7bd735c2fa110"},
|
||||
{file = "coverage-7.2.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3436927d1794fa6763b89b60c896f9e3bd53212001026ebc9080d23f0c2733c1"},
|
||||
{file = "coverage-7.2.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44c9b9f1a245f3d0d202b1a8fa666a80b5ecbe4ad5d0859c0fb16a52d9763224"},
|
||||
{file = "coverage-7.2.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e3783a286d5a93a2921396d50ce45a909aa8f13eee964465012f110f0cbb611"},
|
||||
{file = "coverage-7.2.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cff6980fe7100242170092bb40d2b1cdad79502cd532fd26b12a2b8a5f9aee0"},
|
||||
{file = "coverage-7.2.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c534431153caffc7c495c3eddf7e6a6033e7f81d78385b4e41611b51e8870446"},
|
||||
{file = "coverage-7.2.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3062fd5c62df988cea9f2972c593f77fed1182bfddc5a3b12b1e606cb7aba99e"},
|
||||
{file = "coverage-7.2.6-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6284a2005e4f8061c58c814b1600ad0074ccb0289fe61ea709655c5969877b70"},
|
||||
{file = "coverage-7.2.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:97729e6828643f168a2a3f07848e1b1b94a366b13a9f5aba5484c2215724edc8"},
|
||||
{file = "coverage-7.2.6-cp38-cp38-win32.whl", hash = "sha256:dc11b42fa61ff1e788dd095726a0aed6aad9c03d5c5984b54cb9e1e67b276aa5"},
|
||||
{file = "coverage-7.2.6-cp38-cp38-win_amd64.whl", hash = "sha256:cbcc874f454ee51f158afd604a315f30c0e31dff1d5d5bf499fc529229d964dd"},
|
||||
{file = "coverage-7.2.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d3cacc6a665221108ecdf90517a8028d07a2783df3417d12dcfef1c517e67478"},
|
||||
{file = "coverage-7.2.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:272ab31228a9df857ab5df5d67936d8861464dc89c5d3fab35132626e9369379"},
|
||||
{file = "coverage-7.2.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a8723ccec4e564d4b9a79923246f7b9a8de4ec55fa03ec4ec804459dade3c4f"},
|
||||
{file = "coverage-7.2.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5906f6a84b47f995cd1bf0aca1c72d591c55ee955f98074e93660d64dfc66eb9"},
|
||||
{file = "coverage-7.2.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52c139b7ab3f0b15f9aad0a3fedef5a1f8c0b2bdc291d88639ca2c97d3682416"},
|
||||
{file = "coverage-7.2.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a5ffd45c6b93c23a8507e2f436983015c6457aa832496b6a095505ca2f63e8f1"},
|
||||
{file = "coverage-7.2.6-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4f3c7c19581d471af0e9cb49d928172cd8492cd78a2b7a4e82345d33662929bb"},
|
||||
{file = "coverage-7.2.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8c0e79820cdd67978e1120983786422d279e07a381dbf89d03bbb23ec670a6"},
|
||||
{file = "coverage-7.2.6-cp39-cp39-win32.whl", hash = "sha256:13cde6bb0e58fb67d09e2f373de3899d1d1e866c5a9ff05d93615f2f54fbd2bb"},
|
||||
{file = "coverage-7.2.6-cp39-cp39-win_amd64.whl", hash = "sha256:6b9f64526286255735847aed0221b189486e0b9ed943446936e41b7e44b08783"},
|
||||
{file = "coverage-7.2.6-pp37.pp38.pp39-none-any.whl", hash = "sha256:6babcbf1e66e46052442f10833cfc4a0d3554d8276aa37af8531a83ed3c1a01d"},
|
||||
{file = "coverage-7.2.6.tar.gz", hash = "sha256:2025f913f2edb0272ef15d00b1f335ff8908c921c8eb2013536fcaf61f5a683d"},
|
||||
{file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"},
|
||||
{file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"},
|
||||
{file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"},
|
||||
{file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"},
|
||||
{file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"},
|
||||
{file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"},
|
||||
{file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"},
|
||||
{file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"},
|
||||
{file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"},
|
||||
{file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"},
|
||||
{file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"},
|
||||
{file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"},
|
||||
{file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"},
|
||||
{file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"},
|
||||
{file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"},
|
||||
{file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"},
|
||||
{file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"},
|
||||
{file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"},
|
||||
{file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"},
|
||||
{file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"},
|
||||
{file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"},
|
||||
{file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"},
|
||||
{file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"},
|
||||
{file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"},
|
||||
{file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"},
|
||||
{file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"},
|
||||
{file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"},
|
||||
{file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"},
|
||||
{file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"},
|
||||
{file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"},
|
||||
{file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"},
|
||||
{file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"},
|
||||
{file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"},
|
||||
{file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"},
|
||||
{file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"},
|
||||
{file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"},
|
||||
{file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"},
|
||||
{file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"},
|
||||
{file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"},
|
||||
{file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"},
|
||||
{file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"},
|
||||
{file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"},
|
||||
{file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"},
|
||||
{file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"},
|
||||
{file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"},
|
||||
{file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"},
|
||||
{file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"},
|
||||
{file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"},
|
||||
{file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"},
|
||||
{file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"},
|
||||
{file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"},
|
||||
{file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"},
|
||||
{file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"},
|
||||
{file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"},
|
||||
{file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"},
|
||||
{file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"},
|
||||
{file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"},
|
||||
{file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"},
|
||||
{file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"},
|
||||
{file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@ -1127,14 +1136,14 @@ Django = ">=3.2"
|
||||
|
||||
[[package]]
|
||||
name = "django-otp"
|
||||
version = "1.2.0"
|
||||
version = "1.2.1"
|
||||
description = "A pluggable framework for adding two-factor authentication to Django using one-time passwords."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "django_otp-1.2.0-py3-none-any.whl", hash = "sha256:aa14ace751bede7c6c385f2ea3589f6aa3565a31e455fa0ee69801b79761e3b0"},
|
||||
{file = "django_otp-1.2.0.tar.gz", hash = "sha256:2baa30237f46549446d8d17a790b962f9065168bad38968dd208cdeb85901ede"},
|
||||
{file = "django_otp-1.2.1-py3-none-any.whl", hash = "sha256:1758aabfc17c30ba00142e823c961e20d2a0353040a9e4e4be2688cf71085446"},
|
||||
{file = "django_otp-1.2.1.tar.gz", hash = "sha256:d4785291fc97eb61c02574660f7ae82baab04da187fa5d0fb649d57deea2c62e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -2262,14 +2271,14 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "paramiko"
|
||||
version = "3.1.0"
|
||||
version = "3.2.0"
|
||||
description = "SSH2 protocol library"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "paramiko-3.1.0-py3-none-any.whl", hash = "sha256:f0caa660e797d9cd10db6fc6ae81e2c9b2767af75c3180fcd0e46158cd368d7f"},
|
||||
{file = "paramiko-3.1.0.tar.gz", hash = "sha256:6950faca6819acd3219d4ae694a23c7a87ee38d084f70c1724b0c0dbb8b75769"},
|
||||
{file = "paramiko-3.2.0-py3-none-any.whl", hash = "sha256:df0f9dd8903bc50f2e10580af687f3015bf592a377cd438d2ec9546467a14eb8"},
|
||||
{file = "paramiko-3.2.0.tar.gz", hash = "sha256:93cdce625a8a1dc12204439d45033f3261bdb2c201648cfcdc06f9fd0f94ec29"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -2493,45 +2502,44 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "pycryptodome"
|
||||
version = "3.17"
|
||||
version = "3.18.0"
|
||||
description = "Cryptographic library for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
files = [
|
||||
{file = "pycryptodome-3.17-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:2c5631204ebcc7ae33d11c43037b2dafe25e2ab9c1de6448eb6502ac69c19a56"},
|
||||
{file = "pycryptodome-3.17-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:04779cc588ad8f13c80a060b0b1c9d1c203d051d8a43879117fe6b8aaf1cd3fa"},
|
||||
{file = "pycryptodome-3.17-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:f812d58c5af06d939b2baccdda614a3ffd80531a26e5faca2c9f8b1770b2b7af"},
|
||||
{file = "pycryptodome-3.17-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:9453b4e21e752df8737fdffac619e93c9f0ec55ead9a45df782055eb95ef37d9"},
|
||||
{file = "pycryptodome-3.17-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:121d61663267f73692e8bde5ec0d23c9146465a0d75cad75c34f75c752527b01"},
|
||||
{file = "pycryptodome-3.17-cp27-cp27m-win32.whl", hash = "sha256:ba2d4fcb844c6ba5df4bbfee9352ad5352c5ae939ac450e06cdceff653280450"},
|
||||
{file = "pycryptodome-3.17-cp27-cp27m-win_amd64.whl", hash = "sha256:87e2ca3aa557781447428c4b6c8c937f10ff215202ab40ece5c13a82555c10d6"},
|
||||
{file = "pycryptodome-3.17-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:f44c0d28716d950135ff21505f2c764498eda9d8806b7c78764165848aa419bc"},
|
||||
{file = "pycryptodome-3.17-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:5a790bc045003d89d42e3b9cb3cc938c8561a57a88aaa5691512e8540d1ae79c"},
|
||||
{file = "pycryptodome-3.17-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:d086d46774e27b280e4cece8ab3d87299cf0d39063f00f1e9290d096adc5662a"},
|
||||
{file = "pycryptodome-3.17-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:5587803d5b66dfd99e7caa31ed91fba0fdee3661c5d93684028ad6653fce725f"},
|
||||
{file = "pycryptodome-3.17-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:e7debd9c439e7b84f53be3cf4ba8b75b3d0b6e6015212355d6daf44ac672e210"},
|
||||
{file = "pycryptodome-3.17-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ca1ceb6303be1282148f04ac21cebeebdb4152590842159877778f9cf1634f09"},
|
||||
{file = "pycryptodome-3.17-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:dc22cc00f804485a3c2a7e2010d9f14a705555f67020eb083e833cabd5bd82e4"},
|
||||
{file = "pycryptodome-3.17-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80ea8333b6a5f2d9e856ff2293dba2e3e661197f90bf0f4d5a82a0a6bc83a626"},
|
||||
{file = "pycryptodome-3.17-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c133f6721fba313722a018392a91e3c69d3706ae723484841752559e71d69dc6"},
|
||||
{file = "pycryptodome-3.17-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:333306eaea01fde50a73c4619e25631e56c4c61bd0fb0a2346479e67e3d3a820"},
|
||||
{file = "pycryptodome-3.17-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:1a30f51b990994491cec2d7d237924e5b6bd0d445da9337d77de384ad7f254f9"},
|
||||
{file = "pycryptodome-3.17-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:909e36a43fe4a8a3163e9c7fc103867825d14a2ecb852a63d3905250b308a4e5"},
|
||||
{file = "pycryptodome-3.17-cp35-abi3-win32.whl", hash = "sha256:a3228728a3808bc9f18c1797ec1179a0efb5068c817b2ffcf6bcd012494dffb2"},
|
||||
{file = "pycryptodome-3.17-cp35-abi3-win_amd64.whl", hash = "sha256:9ec565e89a6b400eca814f28d78a9ef3f15aea1df74d95b28b7720739b28f37f"},
|
||||
{file = "pycryptodome-3.17-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:e1819b67bcf6ca48341e9b03c2e45b1c891fa8eb1a8458482d14c2805c9616f2"},
|
||||
{file = "pycryptodome-3.17-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:f8e550caf52472ae9126953415e4fc554ab53049a5691c45b8816895c632e4d7"},
|
||||
{file = "pycryptodome-3.17-pp27-pypy_73-win32.whl", hash = "sha256:afbcdb0eda20a0e1d44e3a1ad6d4ec3c959210f4b48cabc0e387a282f4c7deb8"},
|
||||
{file = "pycryptodome-3.17-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a74f45aee8c5cc4d533e585e0e596e9f78521e1543a302870a27b0ae2106381e"},
|
||||
{file = "pycryptodome-3.17-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38bbd6717eac084408b4094174c0805bdbaba1f57fc250fd0309ae5ec9ed7e09"},
|
||||
{file = "pycryptodome-3.17-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f68d6c8ea2974a571cacb7014dbaada21063a0375318d88ac1f9300bc81e93c3"},
|
||||
{file = "pycryptodome-3.17-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8198f2b04c39d817b206ebe0db25a6653bb5f463c2319d6f6d9a80d012ac1e37"},
|
||||
{file = "pycryptodome-3.17-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3a232474cd89d3f51e4295abe248a8b95d0332d153bf46444e415409070aae1e"},
|
||||
{file = "pycryptodome-3.17-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4992ec965606054e8326e83db1c8654f0549cdb26fce1898dc1a20bc7684ec1c"},
|
||||
{file = "pycryptodome-3.17-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53068e33c74f3b93a8158dacaa5d0f82d254a81b1002e0cd342be89fcb3433eb"},
|
||||
{file = "pycryptodome-3.17-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:74794a2e2896cd0cf56fdc9db61ef755fa812b4a4900fa46c49045663a92b8d0"},
|
||||
{file = "pycryptodome-3.17.tar.gz", hash = "sha256:bce2e2d8e82fcf972005652371a3e8731956a0c1fbb719cc897943b3695ad91b"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:d1497a8cd4728db0e0da3c304856cb37c0c4e3d0b36fcbabcc1600f18504fc54"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:928078c530da78ff08e10eb6cada6e0dff386bf3d9fa9871b4bbc9fbc1efe024"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:157c9b5ba5e21b375f052ca78152dd309a09ed04703fd3721dce3ff8ecced148"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:d20082bdac9218649f6abe0b885927be25a917e29ae0502eaf2b53f1233ce0c2"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:e8ad74044e5f5d2456c11ed4cfd3e34b8d4898c0cb201c4038fe41458a82ea27"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27m-win32.whl", hash = "sha256:62a1e8847fabb5213ccde38915563140a5b338f0d0a0d363f996b51e4a6165cf"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27m-win_amd64.whl", hash = "sha256:16bfd98dbe472c263ed2821284118d899c76968db1a6665ade0c46805e6b29a4"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:7a3d22c8ee63de22336679e021c7f2386f7fc465477d59675caa0e5706387944"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:78d863476e6bad2a592645072cc489bb90320972115d8995bcfbee2f8b209918"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:b6a610f8bfe67eab980d6236fdc73bfcdae23c9ed5548192bb2d530e8a92780e"},
|
||||
{file = "pycryptodome-3.18.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:422c89fd8df8a3bee09fb8d52aaa1e996120eafa565437392b781abec2a56e14"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:9ad6f09f670c466aac94a40798e0e8d1ef2aa04589c29faa5b9b97566611d1d1"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:53aee6be8b9b6da25ccd9028caf17dcdce3604f2c7862f5167777b707fbfb6cb"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:10da29526a2a927c7d64b8f34592f461d92ae55fc97981aab5bbcde8cb465bb6"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f21efb8438971aa16924790e1c3dba3a33164eb4000106a55baaed522c261acf"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4944defabe2ace4803f99543445c27dd1edbe86d7d4edb87b256476a91e9ffa4"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:51eae079ddb9c5f10376b4131be9589a6554f6fd84f7f655180937f611cd99a2"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:83c75952dcf4a4cebaa850fa257d7a860644c70a7cd54262c237c9f2be26f76e"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:957b221d062d5752716923d14e0926f47670e95fead9d240fa4d4862214b9b2f"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-win32.whl", hash = "sha256:795bd1e4258a2c689c0b1f13ce9684fa0dd4c0e08680dcf597cf9516ed6bc0f3"},
|
||||
{file = "pycryptodome-3.18.0-cp35-abi3-win_amd64.whl", hash = "sha256:b1d9701d10303eec8d0bd33fa54d44e67b8be74ab449052a8372f12a66f93fb9"},
|
||||
{file = "pycryptodome-3.18.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:cb1be4d5af7f355e7d41d36d8eec156ef1382a88638e8032215c215b82a4b8ec"},
|
||||
{file = "pycryptodome-3.18.0-pp27-pypy_73-win32.whl", hash = "sha256:fc0a73f4db1e31d4a6d71b672a48f3af458f548059aa05e83022d5f61aac9c08"},
|
||||
{file = "pycryptodome-3.18.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f022a4fd2a5263a5c483a2bb165f9cb27f2be06f2f477113783efe3fe2ad887b"},
|
||||
{file = "pycryptodome-3.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:363dd6f21f848301c2dcdeb3c8ae5f0dee2286a5e952a0f04954b82076f23825"},
|
||||
{file = "pycryptodome-3.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12600268763e6fec3cefe4c2dcdf79bde08d0b6dc1813887e789e495cb9f3403"},
|
||||
{file = "pycryptodome-3.18.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4604816adebd4faf8810782f137f8426bf45fee97d8427fa8e1e49ea78a52e2c"},
|
||||
{file = "pycryptodome-3.18.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:01489bbdf709d993f3058e2996f8f40fee3f0ea4d995002e5968965fa2fe89fb"},
|
||||
{file = "pycryptodome-3.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3811e31e1ac3069988f7a1c9ee7331b942e605dfc0f27330a9ea5997e965efb2"},
|
||||
{file = "pycryptodome-3.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4b967bb11baea9128ec88c3d02f55a3e338361f5e4934f5240afcb667fdaec"},
|
||||
{file = "pycryptodome-3.18.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:9c8eda4f260072f7dbe42f473906c659dcbadd5ae6159dfb49af4da1293ae380"},
|
||||
{file = "pycryptodome-3.18.0.tar.gz", hash = "sha256:c9adee653fc882d98956e33ca2c1fb582e23a8af7ac82fee75bd6113c55a0413"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3013,21 +3021,21 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.28.1"
|
||||
version = "2.31.0"
|
||||
description = "Python HTTP for Humans."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7, <4"
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
|
||||
{file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
|
||||
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
|
||||
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
certifi = ">=2017.4.17"
|
||||
charset-normalizer = ">=2,<3"
|
||||
charset-normalizer = ">=2,<4"
|
||||
idna = ">=2.5,<4"
|
||||
urllib3 = ">=1.21.1,<1.27"
|
||||
urllib3 = ">=1.21.1,<3"
|
||||
|
||||
[package.extras]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
|
||||
@ -3108,29 +3116,29 @@ pyasn1 = ">=0.1.3"
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.267"
|
||||
version = "0.0.270"
|
||||
description = "An extremely fast Python linter, written in Rust."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "ruff-0.0.267-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:4adbbbe314d8fcc539a245065bad89446a3cef2e0c9cf70bf7bb9ed6fe31856d"},
|
||||
{file = "ruff-0.0.267-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:67254ae34c38cba109fdc52e4a70887de1f850fb3971e5eeef343db67305d1c1"},
|
||||
{file = "ruff-0.0.267-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbe104f21a429b77eb5ac276bd5352fd8c0e1fbb580b4c772f77ee8c76825654"},
|
||||
{file = "ruff-0.0.267-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:db33deef2a5e1cf528ca51cc59dd764122a48a19a6c776283b223d147041153f"},
|
||||
{file = "ruff-0.0.267-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9adf1307fa9d840d1acaa477eb04f9702032a483214c409fca9dc46f5f157fe3"},
|
||||
{file = "ruff-0.0.267-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:0afca3633c8e2b6c0a48ad0061180b641b3b404d68d7e6736aab301c8024c424"},
|
||||
{file = "ruff-0.0.267-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2972241065b1c911bce3db808837ed10f4f6f8a8e15520a4242d291083605ab6"},
|
||||
{file = "ruff-0.0.267-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f731d81cb939e757b0335b0090f18ca2e9ff8bcc8e6a1cf909245958949b6e11"},
|
||||
{file = "ruff-0.0.267-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20c594eb56c19063ef5a57f89340e64c6550e169d6a29408a45130a8c3068adc"},
|
||||
{file = "ruff-0.0.267-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:45d61a2b01bdf61581a2ee039503a08aa603dc74a6bbe6fb5d1ce3052f5370e5"},
|
||||
{file = "ruff-0.0.267-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2107cec3699ca4d7bd41543dc1d475c97ae3a21ea9212238b5c2088fa8ee7722"},
|
||||
{file = "ruff-0.0.267-py3-none-musllinux_1_2_i686.whl", hash = "sha256:786de30723c71fc46b80a173c3313fc0dbe73c96bd9da8dd1212cbc2f84cdfb2"},
|
||||
{file = "ruff-0.0.267-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5a898953949e37c109dd242cfcf9841e065319995ebb7cdfd213b446094a942f"},
|
||||
{file = "ruff-0.0.267-py3-none-win32.whl", hash = "sha256:d12ab329474c46b96d962e2bdb92e3ad2144981fe41b89c7770f370646c0101f"},
|
||||
{file = "ruff-0.0.267-py3-none-win_amd64.whl", hash = "sha256:d09aecc9f5845586ba90911d815f9772c5a6dcf2e34be58c6017ecb124534ac4"},
|
||||
{file = "ruff-0.0.267-py3-none-win_arm64.whl", hash = "sha256:7df7eb5f8d791566ba97cc0b144981b9c080a5b861abaf4bb35a26c8a77b83e9"},
|
||||
{file = "ruff-0.0.267.tar.gz", hash = "sha256:632cec7bbaf3c06fcf0a72a1dd029b7d8b7f424ba95a574aaa135f5d20a00af7"},
|
||||
{file = "ruff-0.0.270-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:f74c4d550f7b8e808455ac77bbce38daafc458434815ba0bc21ae4bdb276509b"},
|
||||
{file = "ruff-0.0.270-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:643de865fd35cb76c4f0739aea5afe7b8e4d40d623df7e9e6ea99054e5cead0a"},
|
||||
{file = "ruff-0.0.270-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eca02e709b3308eb7255b5f74e779be23b5980fca3862eae28bb23069cd61ae4"},
|
||||
{file = "ruff-0.0.270-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3ed3b198768d2b3a2300fb18f730cd39948a5cc36ba29ae9d4639a11040880be"},
|
||||
{file = "ruff-0.0.270-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:739495d2dbde87cf4e3110c8d27bc20febf93112539a968a4e02c26f0deccd1d"},
|
||||
{file = "ruff-0.0.270-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:08188f8351f4c0b6216e8463df0a76eb57894ca59a3da65e4ed205db980fd3ae"},
|
||||
{file = "ruff-0.0.270-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0827b074635d37984fc98d99316bfab5c8b1231bb83e60dacc83bd92883eedb4"},
|
||||
{file = "ruff-0.0.270-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d61ae4841313f6eeb8292dc349bef27b4ce426e62c36e80ceedc3824e408734"},
|
||||
{file = "ruff-0.0.270-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0eb412f20e77529a01fb94d578b19dcb8331b56f93632aa0cce4a2ea27b7aeba"},
|
||||
{file = "ruff-0.0.270-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b775e2c5fc869359daf8c8b8aa0fd67240201ab2e8d536d14a0edf279af18786"},
|
||||
{file = "ruff-0.0.270-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:21f00e47ab2308617c44435c8dfd9e2e03897461c9e647ec942deb2a235b4cfd"},
|
||||
{file = "ruff-0.0.270-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0bbfbf6fd2436165566ca85f6e57be03ed2f0a994faf40180cfbb3604c9232ef"},
|
||||
{file = "ruff-0.0.270-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8af391ef81f7be960be10886a3c1aac0b298bde7cb9a86ec2b05faeb2081ce6b"},
|
||||
{file = "ruff-0.0.270-py3-none-win32.whl", hash = "sha256:b4c037fe2f75bcd9aed0c89c7c507cb7fa59abae2bd4c8b6fc331a28178655a4"},
|
||||
{file = "ruff-0.0.270-py3-none-win_amd64.whl", hash = "sha256:0012f9b7dc137ab7f1f0355e3c4ca49b562baf6c9fa1180948deeb6648c52957"},
|
||||
{file = "ruff-0.0.270-py3-none-win_arm64.whl", hash = "sha256:9613456b0b375766244c25045e353bc8890c856431cd97893c97b10cc93bd28d"},
|
||||
{file = "ruff-0.0.270.tar.gz", hash = "sha256:95db07b7850b30ebf32b27fe98bc39e0ab99db3985edbbf0754d399eb2f0e690"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3153,14 +3161,14 @@ urllib3 = {version = ">=1.26,<3", extras = ["socks"]}
|
||||
|
||||
[[package]]
|
||||
name = "sentry-sdk"
|
||||
version = "1.23.1"
|
||||
version = "1.24.0"
|
||||
description = "Python client for Sentry (https://sentry.io)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "sentry-sdk-1.23.1.tar.gz", hash = "sha256:0300fbe7a07b3865b3885929fb863a68ff01f59e3bcfb4e7953d0bf7fd19c67f"},
|
||||
{file = "sentry_sdk-1.23.1-py2.py3-none-any.whl", hash = "sha256:a884e2478e0b055776ea2b9234d5de9339b4bae0b3a5e74ae43d131db8ded27e"},
|
||||
{file = "sentry-sdk-1.24.0.tar.gz", hash = "sha256:0bbcecda9f51936904c1030e7fef0fe693e633888f02a14d1cb68646a50e83b3"},
|
||||
{file = "sentry_sdk-1.24.0-py2.py3-none-any.whl", hash = "sha256:56d6d9d194c898d853a7c1dd99bed92ce82334ee1282292c15bcc967ff1a49b5"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -3377,23 +3385,23 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "tornado"
|
||||
version = "6.2"
|
||||
version = "6.3.2"
|
||||
description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">= 3.7"
|
||||
python-versions = ">= 3.8"
|
||||
files = [
|
||||
{file = "tornado-6.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72"},
|
||||
{file = "tornado-6.2-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9"},
|
||||
{file = "tornado-6.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac"},
|
||||
{file = "tornado-6.2-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8150f721c101abdef99073bf66d3903e292d851bee51910839831caba341a75"},
|
||||
{file = "tornado-6.2-cp37-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3a2f5999215a3a06a4fc218026cd84c61b8b2b40ac5296a6db1f1451ef04c1e"},
|
||||
{file = "tornado-6.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c52d219d4995388119af7ccaa0bcec289535747620116a58d830e7c25d8a8"},
|
||||
{file = "tornado-6.2-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:6fdfabffd8dfcb6cf887428849d30cf19a3ea34c2c248461e1f7d718ad30b66b"},
|
||||
{file = "tornado-6.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:1d54d13ab8414ed44de07efecb97d4ef7c39f7438cf5e976ccd356bebb1b5fca"},
|
||||
{file = "tornado-6.2-cp37-abi3-win32.whl", hash = "sha256:5c87076709343557ef8032934ce5f637dbb552efa7b21d08e89ae7619ed0eb23"},
|
||||
{file = "tornado-6.2-cp37-abi3-win_amd64.whl", hash = "sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b"},
|
||||
{file = "tornado-6.2.tar.gz", hash = "sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13"},
|
||||
{file = "tornado-6.3.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:c367ab6c0393d71171123ca5515c61ff62fe09024fa6bf299cd1339dc9456829"},
|
||||
{file = "tornado-6.3.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b46a6ab20f5c7c1cb949c72c1994a4585d2eaa0be4853f50a03b5031e964fc7c"},
|
||||
{file = "tornado-6.3.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2de14066c4a38b4ecbbcd55c5cc4b5340eb04f1c5e81da7451ef555859c833f"},
|
||||
{file = "tornado-6.3.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05615096845cf50a895026f749195bf0b10b8909f9be672f50b0fe69cba368e4"},
|
||||
{file = "tornado-6.3.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b17b1cf5f8354efa3d37c6e28fdfd9c1c1e5122f2cb56dac121ac61baa47cbe"},
|
||||
{file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:29e71c847a35f6e10ca3b5c2990a52ce38b233019d8e858b755ea6ce4dcdd19d"},
|
||||
{file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:834ae7540ad3a83199a8da8f9f2d383e3c3d5130a328889e4cc991acc81e87a0"},
|
||||
{file = "tornado-6.3.2-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6a0848f1aea0d196a7c4f6772197cbe2abc4266f836b0aac76947872cd29b411"},
|
||||
{file = "tornado-6.3.2-cp38-abi3-win32.whl", hash = "sha256:7efcbcc30b7c654eb6a8c9c9da787a851c18f8ccd4a5a3a95b05c7accfa068d2"},
|
||||
{file = "tornado-6.3.2-cp38-abi3-win_amd64.whl", hash = "sha256:0c325e66c8123c606eea33084976c832aa4e766b7dff8aedd7587ea44a604cdf"},
|
||||
{file = "tornado-6.3.2.tar.gz", hash = "sha256:4b927c4f19b71e627b13f3db2324e4ae660527143f9e1f2e2fb404f3a187e2ba"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3436,14 +3444,14 @@ wsproto = ">=0.14"
|
||||
|
||||
[[package]]
|
||||
name = "twilio"
|
||||
version = "8.2.0"
|
||||
version = "8.2.1"
|
||||
description = "Twilio API client and TwiML generator"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
files = [
|
||||
{file = "twilio-8.2.0-py2.py3-none-any.whl", hash = "sha256:23eceaec183995fc827e3bfad229cca6e1944bfd9604e57e2712e625b6e01223"},
|
||||
{file = "twilio-8.2.0.tar.gz", hash = "sha256:0c19eb6a5b84dbcd15658e23a142df026297236e4d72ad9304fd95e7dbff2662"},
|
||||
{file = "twilio-8.2.1-py2.py3-none-any.whl", hash = "sha256:9c6bbfda1f3196c64258b6661e372c23e2e6a9630c9986f725d16bb4bfe3275f"},
|
||||
{file = "twilio-8.2.1.tar.gz", hash = "sha256:66fe6a18199955b8abce2699e533b56f605a22d585c3f0b1820113ec068a0b51"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -3584,14 +3592,14 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "1.26.15"
|
||||
version = "1.26.16"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
|
||||
files = [
|
||||
{file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"},
|
||||
{file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"},
|
||||
{file = "urllib3-1.26.16-py2.py3-none-any.whl", hash = "sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f"},
|
||||
{file = "urllib3-1.26.16.tar.gz", hash = "sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
38
schema.yml
38
schema.yml
@ -18450,6 +18450,11 @@ paths:
|
||||
* `email_deny` - Use the user's email address, but deny enrollment when the email address already exists.
|
||||
* `username_link` - Link to a user with identical username. Can have security implications when a username is used with another source.
|
||||
* `username_deny` - Use the user's username, but deny enrollment when the username already exists.
|
||||
- in: query
|
||||
name: verification_kp
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
tags:
|
||||
- sources
|
||||
security:
|
||||
@ -37280,13 +37285,20 @@ components:
|
||||
* `urn:oasis:names:tc:SAML:2.0:nameid-format:transient` - Transient
|
||||
binding_type:
|
||||
$ref: '#/components/schemas/BindingTypeEnum'
|
||||
verification_kp:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
title: Verification Certificate
|
||||
description: When selected, incoming assertion's Signatures will be validated
|
||||
against this certificate. To allow unsigned Requests, leave on default.
|
||||
signing_kp:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
title: Signing Keypair
|
||||
description: Keypair which is used to sign outgoing requests. Leave empty
|
||||
to disable signing.
|
||||
description: Keypair used to sign outgoing Responses going to the Identity
|
||||
Provider.
|
||||
digest_algorithm:
|
||||
$ref: '#/components/schemas/DigestAlgorithmEnum'
|
||||
signature_algorithm:
|
||||
@ -39608,13 +39620,20 @@ components:
|
||||
* `urn:oasis:names:tc:SAML:2.0:nameid-format:transient` - Transient
|
||||
binding_type:
|
||||
$ref: '#/components/schemas/BindingTypeEnum'
|
||||
verification_kp:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
title: Verification Certificate
|
||||
description: When selected, incoming assertion's Signatures will be validated
|
||||
against this certificate. To allow unsigned Requests, leave on default.
|
||||
signing_kp:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
title: Signing Keypair
|
||||
description: Keypair which is used to sign outgoing requests. Leave empty
|
||||
to disable signing.
|
||||
description: Keypair used to sign outgoing Responses going to the Identity
|
||||
Provider.
|
||||
digest_algorithm:
|
||||
$ref: '#/components/schemas/DigestAlgorithmEnum'
|
||||
signature_algorithm:
|
||||
@ -39715,13 +39734,20 @@ components:
|
||||
* `urn:oasis:names:tc:SAML:2.0:nameid-format:transient` - Transient
|
||||
binding_type:
|
||||
$ref: '#/components/schemas/BindingTypeEnum'
|
||||
verification_kp:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
title: Verification Certificate
|
||||
description: When selected, incoming assertion's Signatures will be validated
|
||||
against this certificate. To allow unsigned Requests, leave on default.
|
||||
signing_kp:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
title: Signing Keypair
|
||||
description: Keypair which is used to sign outgoing requests. Leave empty
|
||||
to disable signing.
|
||||
description: Keypair used to sign outgoing Responses going to the Identity
|
||||
Provider.
|
||||
digest_algorithm:
|
||||
$ref: '#/components/schemas/DigestAlgorithmEnum'
|
||||
signature_algorithm:
|
||||
|
1895
web/package-lock.json
generated
1895
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -24,15 +24,15 @@
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@formatjs/intl-listformat": "^7.2.2",
|
||||
"@fortawesome/fontawesome-free": "^6.4.0",
|
||||
"@goauthentik/api": "^2023.5.0-1684333401",
|
||||
"@lingui/cli": "^4.1.2",
|
||||
"@lingui/core": "^4.1.2",
|
||||
"@lingui/detect-locale": "^4.1.2",
|
||||
"@lingui/format-po-gettext": "^4.1.2",
|
||||
"@lingui/macro": "^4.1.2",
|
||||
"@goauthentik/api": "^2023.5.2-1685273038",
|
||||
"@lingui/cli": "^4.2.0",
|
||||
"@lingui/core": "^4.2.0",
|
||||
"@lingui/detect-locale": "^4.2.0",
|
||||
"@lingui/format-po-gettext": "^4.2.0",
|
||||
"@lingui/macro": "^4.2.0",
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@sentry/browser": "^7.52.1",
|
||||
"@sentry/tracing": "^7.52.1",
|
||||
"@sentry/browser": "^7.53.1",
|
||||
"@sentry/tracing": "^7.53.1",
|
||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"chart.js": "^4.3.0",
|
||||
@ -43,16 +43,18 @@
|
||||
"country-flag-icons": "^1.5.7",
|
||||
"fuse.js": "^6.6.2",
|
||||
"lit": "^2.7.4",
|
||||
"mermaid": "^10.1.0",
|
||||
"mermaid": "^10.2.0",
|
||||
"rapidoc": "^9.3.4",
|
||||
"webcomponent-qr-code": "^1.1.1",
|
||||
"yaml": "^2.2.2"
|
||||
"yaml": "^2.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.21.8",
|
||||
"@babel/plugin-proposal-decorators": "^7.21.0",
|
||||
"@babel/plugin-transform-runtime": "^7.21.4",
|
||||
"@babel/preset-env": "^7.21.5",
|
||||
"@babel/core": "^7.22.1",
|
||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||
"@babel/plugin-proposal-decorators": "^7.22.3",
|
||||
"@babel/plugin-proposal-private-methods": "^7.18.6",
|
||||
"@babel/plugin-transform-runtime": "^7.22.4",
|
||||
"@babel/preset-env": "^7.22.4",
|
||||
"@babel/preset-typescript": "^7.21.5",
|
||||
"@hcaptcha/types": "^1.0.3",
|
||||
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
|
||||
@ -66,23 +68,23 @@
|
||||
"@types/chart.js": "^2.9.37",
|
||||
"@types/codemirror": "5.60.7",
|
||||
"@types/grecaptcha": "^3.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.6",
|
||||
"@typescript-eslint/parser": "^5.59.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.8",
|
||||
"@typescript-eslint/parser": "^5.59.8",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"babel-plugin-tsconfig-paths": "^1.0.3",
|
||||
"eslint": "^8.40.0",
|
||||
"eslint": "^8.41.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-custom-elements": "0.0.8",
|
||||
"eslint-plugin-lit": "^1.8.3",
|
||||
"prettier": "^2.8.8",
|
||||
"pyright": "^1.1.308",
|
||||
"pyright": "^1.1.310",
|
||||
"rollup": "^2.79.1",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
"rollup-plugin-cssimport": "^1.0.3",
|
||||
"rollup-plugin-minify-html-literals": "^1.2.6",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"ts-lit-plugin": "^1.2.1",
|
||||
"tslib": "^2.5.1",
|
||||
"tslib": "^2.5.2",
|
||||
"turnstile-types": "^1.1.2",
|
||||
"typescript": "^5.0.4"
|
||||
}
|
||||
|
@ -304,6 +304,42 @@ export class SAMLSourceForm extends ModelForm<SAMLSource, string> {
|
||||
${t`Keypair which is used to sign outgoing requests. Leave empty to disable signing.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Verification Certificate`}
|
||||
name="verificationKp"
|
||||
>
|
||||
<ak-search-select
|
||||
.fetchObjects=${async (
|
||||
query?: string,
|
||||
): Promise<CertificateKeyPair[]> => {
|
||||
const args: CryptoCertificatekeypairsListRequest = {
|
||||
ordering: "name",
|
||||
includeDetails: false,
|
||||
};
|
||||
if (query !== undefined) {
|
||||
args.search = query;
|
||||
}
|
||||
const certificates = await new CryptoApi(
|
||||
DEFAULT_CONFIG,
|
||||
).cryptoCertificatekeypairsList(args);
|
||||
return certificates.results;
|
||||
}}
|
||||
.renderElement=${(item: CertificateKeyPair): string => {
|
||||
return item.name;
|
||||
}}
|
||||
.value=${(item: CertificateKeyPair | undefined): string | undefined => {
|
||||
return item?.pk;
|
||||
}}
|
||||
.selected=${(item: CertificateKeyPair): boolean => {
|
||||
return item.pk === this.instance?.verificationKp;
|
||||
}}
|
||||
?blankable=${true}
|
||||
>
|
||||
</ak-search-select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group>
|
||||
|
@ -39,10 +39,7 @@ export class RedirectStage extends BaseStage<RedirectChallenge, FlowChallengeRes
|
||||
}
|
||||
|
||||
getURL(): string {
|
||||
if (!this.challenge.to.includes("://")) {
|
||||
return window.location.origin + this.challenge.to;
|
||||
}
|
||||
return this.challenge.to;
|
||||
return new URL(this.challenge.to, document.baseURI).toString();
|
||||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
|
@ -202,7 +202,7 @@ export class IdentificationStage extends BaseStage<
|
||||
}
|
||||
|
||||
renderInput(): TemplateResult {
|
||||
let type = "text";
|
||||
let type: "text" | "email" = "text";
|
||||
if (!this.challenge?.userFields || this.challenge.userFields.length === 0) {
|
||||
return html`<p>${t`Select one of the sources below to login.`}</p>`;
|
||||
}
|
||||
|
@ -64,32 +64,32 @@ export class PromptStage extends BaseStage<PromptChallenge, PromptChallengeRespo
|
||||
/>`;
|
||||
case PromptTypeEnum.TextArea:
|
||||
return html`<textarea
|
||||
type="text"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
autocomplete="off"
|
||||
class="pf-c-form-control"
|
||||
?required=${prompt.required}
|
||||
value="${prompt.initialValue}"
|
||||
></textarea>`;
|
||||
>
|
||||
${prompt.initialValue}</textarea
|
||||
>`;
|
||||
case PromptTypeEnum.TextReadOnly:
|
||||
return html`<input
|
||||
type="text"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
class="pf-c-form-control"
|
||||
readonly
|
||||
?readonly=${true}
|
||||
value="${prompt.initialValue}"
|
||||
/>`;
|
||||
case PromptTypeEnum.TextAreaReadOnly:
|
||||
return html`<textarea
|
||||
type="text"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
class="pf-c-form-control"
|
||||
readonly
|
||||
value="${prompt.initialValue}"
|
||||
></textarea>`;
|
||||
>
|
||||
${prompt.initialValue}</textarea
|
||||
>`;
|
||||
case PromptTypeEnum.Username:
|
||||
return html`<input
|
||||
type="text"
|
||||
@ -186,8 +186,8 @@ export class PromptStage extends BaseStage<PromptChallenge, PromptChallengeRespo
|
||||
class="pf-c-check__input"
|
||||
name="${prompt.fieldKey}"
|
||||
id="${id}"
|
||||
checked="${prompt.initialValue === choice}"
|
||||
required="${prompt.required}"
|
||||
?checked="${prompt.initialValue === choice}"
|
||||
?required="${prompt.required}"
|
||||
value="${choice}"
|
||||
/>
|
||||
<label class="pf-c-check__label" for=${id}>${choice}</label>
|
||||
@ -195,7 +195,7 @@ export class PromptStage extends BaseStage<PromptChallenge, PromptChallengeRespo
|
||||
})}`;
|
||||
case PromptTypeEnum.AkLocale:
|
||||
return html`<select class="pf-c-form-control" name="${prompt.fieldKey}">
|
||||
<option value="" ${prompt.initialValue === "" ? "selected" : ""}>
|
||||
<option value="" ?selected=${prompt.initialValue === ""}>
|
||||
${t`Auto-detect (based on your browser)`}
|
||||
</option>
|
||||
${LOCALES.filter((locale) => {
|
||||
@ -209,7 +209,7 @@ export class PromptStage extends BaseStage<PromptChallenge, PromptChallengeRespo
|
||||
}).map((locale) => {
|
||||
return html`<option
|
||||
value=${locale.code}
|
||||
${prompt.initialValue === locale.code ? "selected" : ""}
|
||||
?selected=${prompt.initialValue === locale.code}
|
||||
>
|
||||
${locale.code.toUpperCase()} - ${locale.label}
|
||||
</option>`;
|
||||
|
@ -7766,6 +7766,7 @@ msgid "Verification"
|
||||
msgstr "Überprüfung"
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Verification Certificate"
|
||||
msgstr "Zertifikat zur Überprüfung"
|
||||
|
||||
@ -7991,6 +7992,7 @@ msgid "When selected, a password field is shown on the same page instead of a se
|
||||
msgstr "Wenn diese Option ausgewählt ist, wird ein Passwortfeld auf derselben Seite statt auf einer separaten Seite angezeigt. Dadurch werden Angriffe auf die Aufzählung von Benutzernamen verhindert."
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default."
|
||||
msgstr "Wenn diese Option ausgewählt ist, werden die Signaturen eingehender Behauptungen anhand dieses Zertifikats validiert. Um nicht signierte Anfragen zuzulassen, belassen Sie die Standardeinstellung."
|
||||
|
||||
|
@ -7930,6 +7930,7 @@ msgid "Verification"
|
||||
msgstr "Verification"
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Verification Certificate"
|
||||
msgstr "Verification Certificate"
|
||||
|
||||
@ -8164,6 +8165,7 @@ msgid "When selected, a password field is shown on the same page instead of a se
|
||||
msgstr "When selected, a password field is shown on the same page instead of a separate page. This prevents username enumeration attacks."
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default."
|
||||
msgstr "When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default."
|
||||
|
||||
|
@ -7742,6 +7742,7 @@ msgid "Verification"
|
||||
msgstr "Verificación"
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Verification Certificate"
|
||||
msgstr "Certificado de verificación"
|
||||
|
||||
@ -7967,6 +7968,7 @@ msgid "When selected, a password field is shown on the same page instead of a se
|
||||
msgstr "Cuando se selecciona, se muestra un campo de contraseña en la misma página en lugar de en una página separada. Esto evita ataques de enumeración de nombres de usuario."
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default."
|
||||
msgstr "Cuando se selecciona, las firmas de la aserción entrante se validarán con este certificado. Para permitir solicitudes sin firmar, déjelo en el valor predeterminado."
|
||||
|
||||
|
@ -7733,6 +7733,7 @@ msgid "Verification"
|
||||
msgstr "Vérification"
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Verification Certificate"
|
||||
msgstr "Certificat de validation"
|
||||
|
||||
@ -7958,6 +7959,7 @@ msgid "When selected, a password field is shown on the same page instead of a se
|
||||
msgstr "Si activée, un champ de mot de passe est affiché sur la même page au lieu d'une page séparée. Cela permet d'éviter les attaques par énumération de noms d'utilisateur."
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default."
|
||||
msgstr "Si activée, les signatures des assertions entrantes seront validées par rapport à ce certificat. Pour autoriser les requêtes non signées, laissez la valeur par défaut."
|
||||
|
||||
|
@ -7752,6 +7752,7 @@ msgid "Verification"
|
||||
msgstr "Weryfikacja"
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Verification Certificate"
|
||||
msgstr "Certyfikat weryfikacji"
|
||||
|
||||
@ -7979,6 +7980,7 @@ msgid "When selected, a password field is shown on the same page instead of a se
|
||||
msgstr "Po wybraniu pole hasła jest wyświetlane na tej samej stronie zamiast na osobnej stronie. Zapobiega to atakom polegającym na wyliczaniu nazw użytkowników."
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default."
|
||||
msgstr "Po wybraniu, przychodzące podpisy asercji będą sprawdzane względem tego certyfikatu. Aby zezwolić na niepodpisane żądania, pozostaw domyślnie."
|
||||
|
||||
|
@ -7888,6 +7888,7 @@ msgid "Verification"
|
||||
msgstr ""
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Verification Certificate"
|
||||
msgstr ""
|
||||
|
||||
@ -8116,6 +8117,7 @@ msgid "When selected, a password field is shown on the same page instead of a se
|
||||
msgstr ""
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default."
|
||||
msgstr ""
|
||||
|
||||
|
@ -7742,6 +7742,7 @@ msgid "Verification"
|
||||
msgstr "Doğrulama"
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Verification Certificate"
|
||||
msgstr "Doğrulama Sertifikası"
|
||||
|
||||
@ -7967,6 +7968,7 @@ msgid "When selected, a password field is shown on the same page instead of a se
|
||||
msgstr "Seçildiğinde, ayrı bir sayfa yerine aynı sayfada bir parola alanı gösterilir. Bu, kullanıcı adı numaralandırma saldırılarını engeller."
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default."
|
||||
msgstr "Seçildiğinde, gelen onaylama öğesinin İmzaları bu sertifikaya göre doğrulanır. İmzasız İsteklere izin vermek için varsayılan olarak bırakın."
|
||||
|
||||
|
@ -757,7 +757,7 @@ msgstr "身份验证"
|
||||
|
||||
#: src/flow/stages/authenticator_validate/AuthenticatorValidateStageCode.ts
|
||||
msgid "Authentication code"
|
||||
msgstr ""
|
||||
msgstr "身份验证代码"
|
||||
|
||||
#: src/admin/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/admin/providers/proxy/ProxyProviderForm.ts
|
||||
@ -4473,7 +4473,7 @@ msgstr "打开设置"
|
||||
|
||||
#: src/flow/stages/authenticator_validate/AuthenticatorValidateStageCode.ts
|
||||
msgid "Open your two-factor authenticator app to view your authentication code."
|
||||
msgstr ""
|
||||
msgstr "打开您的两步验证应用查看身份验证代码。"
|
||||
|
||||
#: src/admin/providers/oauth2/OAuth2ProviderViewPage.ts
|
||||
msgid "OpenID Configuration Issuer"
|
||||
@ -4759,7 +4759,7 @@ msgstr "请输入您通过短信收到的验证码"
|
||||
|
||||
#: src/flow/stages/authenticator_validate/AuthenticatorValidateStageCode.ts
|
||||
msgid "Please enter your code"
|
||||
msgstr ""
|
||||
msgstr "请输入您的代码"
|
||||
|
||||
#: src/flow/providers/oauth2/DeviceCode.ts
|
||||
msgid "Please enter your Code"
|
||||
@ -7641,6 +7641,7 @@ msgid "Verification"
|
||||
msgstr "验证"
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Verification Certificate"
|
||||
msgstr "验证证书"
|
||||
|
||||
@ -7724,7 +7725,7 @@ msgstr "警告:策略未分配。"
|
||||
|
||||
#: src/admin/providers/scim/SCIMProviderViewPage.ts
|
||||
msgid "Warning: Provider is not assigned to an application as backchannel provider."
|
||||
msgstr ""
|
||||
msgstr "警告:提供程序未作为反向通道分配给应用程序。"
|
||||
|
||||
#: src/admin/providers/oauth2/OAuth2ProviderViewPage.ts
|
||||
#: src/admin/providers/proxy/ProxyProviderViewPage.ts
|
||||
@ -7868,6 +7869,7 @@ msgid "When selected, a password field is shown on the same page instead of a se
|
||||
msgstr "选中后,密码字段将显示在同一页面,而不是单独的页面上。这样可以防止用户名枚举攻击。"
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default."
|
||||
msgstr "选中后,传入断言的签名将根据此证书进行验证。要允许未签名的请求,请保留默认值。"
|
||||
|
||||
|
@ -7750,6 +7750,7 @@ msgid "Verification"
|
||||
msgstr "验证"
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Verification Certificate"
|
||||
msgstr "验证证书"
|
||||
|
||||
@ -7977,6 +7978,7 @@ msgid "When selected, a password field is shown on the same page instead of a se
|
||||
msgstr "选中后,密码字段将显示在同一页面上,而不是单独的页面上。这样可以防止用户名枚举攻击。"
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default."
|
||||
msgstr "选中后,传入声明的签名将根据此证书进行验证。要允许未签名的请求,请保留默认值。"
|
||||
|
||||
|
@ -385,7 +385,7 @@ msgstr "高级设置"
|
||||
msgid "Affected model:"
|
||||
msgstr "受影响的模型:"
|
||||
|
||||
#: src/admin/events/RuleListPage.ts
|
||||
#: src/admin/events/utils.ts
|
||||
msgid "Alert"
|
||||
msgstr "注意"
|
||||
|
||||
@ -757,6 +757,10 @@ msgstr "正在使用 Plex 进行身份验证..."
|
||||
msgid "Authentication"
|
||||
msgstr "身份验证"
|
||||
|
||||
#: src/flow/stages/authenticator_validate/AuthenticatorValidateStageCode.ts
|
||||
msgid "Authentication code"
|
||||
msgstr "身份验证代码"
|
||||
|
||||
#: src/admin/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/admin/providers/proxy/ProxyProviderForm.ts
|
||||
#: src/admin/providers/radius/RadiusProviderForm.ts
|
||||
@ -1387,7 +1391,6 @@ msgstr "关闭"
|
||||
#: src/flow/providers/oauth2/DeviceCode.ts
|
||||
#: src/flow/stages/authenticator_sms/AuthenticatorSMSStage.ts
|
||||
#: src/flow/stages/authenticator_totp/AuthenticatorTOTPStage.ts
|
||||
#: src/flow/stages/authenticator_validate/AuthenticatorValidateStageCode.ts
|
||||
msgid "Code"
|
||||
msgstr "代码"
|
||||
|
||||
@ -4380,7 +4383,7 @@ msgstr "不是您?"
|
||||
msgid "Notes"
|
||||
msgstr "备注"
|
||||
|
||||
#: src/admin/events/RuleListPage.ts
|
||||
#: src/admin/events/utils.ts
|
||||
msgid "Notice"
|
||||
msgstr "通知"
|
||||
|
||||
@ -4549,6 +4552,11 @@ msgstr "打开登录"
|
||||
msgid "Open settings"
|
||||
msgstr "打开设置"
|
||||
|
||||
#: src/flow/stages/authenticator_validate/AuthenticatorValidateStageCode.ts
|
||||
msgid ""
|
||||
"Open your two-factor authenticator app to view your authentication code."
|
||||
msgstr "打开您的两步验证应用查看身份验证代码。"
|
||||
|
||||
#: src/admin/providers/oauth2/OAuth2ProviderViewPage.ts
|
||||
msgid "OpenID Configuration Issuer"
|
||||
msgstr "OpenID 配置颁发者"
|
||||
@ -4851,8 +4859,11 @@ msgstr "规划历史记录"
|
||||
msgid "Please enter the code you received via SMS"
|
||||
msgstr "请输入您通过短信收到的验证码"
|
||||
|
||||
#: src/flow/providers/oauth2/DeviceCode.ts
|
||||
#: src/flow/stages/authenticator_validate/AuthenticatorValidateStageCode.ts
|
||||
msgid "Please enter your code"
|
||||
msgstr "请输入您的代码"
|
||||
|
||||
#: src/flow/providers/oauth2/DeviceCode.ts
|
||||
msgid "Please enter your Code"
|
||||
msgstr "请输入您的验证码"
|
||||
|
||||
@ -7252,7 +7263,7 @@ msgstr "未知提供程序类型"
|
||||
msgid "Unknown proxy mode"
|
||||
msgstr "未知代理模式"
|
||||
|
||||
#: src/admin/events/RuleListPage.ts src/admin/events/RuleListPage.ts
|
||||
#: src/admin/events/utils.ts src/admin/events/utils.ts
|
||||
msgid "Unknown severity"
|
||||
msgstr "未知严重程度"
|
||||
|
||||
@ -7909,7 +7920,7 @@ msgstr "正在等待身份验证…"
|
||||
#: src/admin/admin-overview/cards/SystemStatusCard.ts
|
||||
#: src/admin/admin-overview/cards/SystemStatusCard.ts
|
||||
#: src/admin/admin-overview/cards/SystemStatusCard.ts
|
||||
#: src/admin/blueprints/BlueprintListPage.ts src/admin/events/RuleListPage.ts
|
||||
#: src/admin/blueprints/BlueprintListPage.ts src/admin/events/utils.ts
|
||||
#: src/admin/system-tasks/SystemTaskListPage.ts
|
||||
msgid "Warning"
|
||||
msgstr "警告"
|
||||
@ -7939,6 +7950,11 @@ msgstr "警告:没有邀请阶段绑定到任何流程。邀请将无法按预
|
||||
msgid "Warning: Policy is not assigned."
|
||||
msgstr "警告:策略未分配。"
|
||||
|
||||
#: src/admin/providers/scim/SCIMProviderViewPage.ts
|
||||
msgid ""
|
||||
"Warning: Provider is not assigned to an application as backchannel provider."
|
||||
msgstr "警告:提供程序未作为反向通道分配给应用程序。"
|
||||
|
||||
#: src/admin/providers/oauth2/OAuth2ProviderViewPage.ts
|
||||
#: src/admin/providers/proxy/ProxyProviderViewPage.ts
|
||||
#: src/admin/providers/saml/SAMLProviderViewPage.ts
|
||||
@ -7947,6 +7963,7 @@ msgstr "警告:提供程序未被任何应用程序使用。"
|
||||
|
||||
#: src/admin/providers/ldap/LDAPProviderViewPage.ts
|
||||
#: src/admin/providers/proxy/ProxyProviderViewPage.ts
|
||||
#: src/admin/providers/radius/RadiusProviderViewPage.ts
|
||||
msgid "Warning: Provider is not used by any Outpost."
|
||||
msgstr "警告:提供程序未被任何前哨使用。"
|
||||
|
||||
|
@ -7750,6 +7750,7 @@ msgid "Verification"
|
||||
msgstr "验证"
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "Verification Certificate"
|
||||
msgstr "验证证书"
|
||||
|
||||
@ -7977,6 +7978,7 @@ msgid "When selected, a password field is shown on the same page instead of a se
|
||||
msgstr "选中后,密码字段将显示在同一页面上,而不是单独的页面上。这样可以防止用户名枚举攻击。"
|
||||
|
||||
#: src/admin/providers/saml/SAMLProviderForm.ts
|
||||
#: src/admin/sources/saml/SAMLSourceForm.ts
|
||||
msgid "When selected, incoming assertion's Signatures will be validated against this certificate. To allow unsigned Requests, leave on default."
|
||||
msgstr "选中后,传入声明的签名将根据此证书进行验证。要允许未签名的请求,请保留默认值。"
|
||||
|
||||
|
@ -1,248 +0,0 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { LayoutType } from "@goauthentik/common/ui/config";
|
||||
import { groupBy } from "@goauthentik/common/utils";
|
||||
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/EmptyState";
|
||||
import { getURLParam, updateURLParams } from "@goauthentik/elements/router/RouteMatch";
|
||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||
import "@goauthentik/user/LibraryApplication";
|
||||
import Fuse from "fuse.js";
|
||||
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";
|
||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||
import PFGallery from "@patternfly/patternfly/layouts/Gallery/gallery.css";
|
||||
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
|
||||
|
||||
import { Application, CoreApi } from "@goauthentik/api";
|
||||
|
||||
export function loading<T>(v: T, actual: TemplateResult): TemplateResult {
|
||||
if (!v) {
|
||||
return html`<ak-empty-state ?loading="${true}" header=${t`Loading`}> </ak-empty-state>`;
|
||||
}
|
||||
return actual;
|
||||
}
|
||||
|
||||
@customElement("ak-library")
|
||||
export class LibraryPage extends AKElement {
|
||||
@property({ attribute: false })
|
||||
apps?: PaginatedResponse<Application>;
|
||||
|
||||
@state()
|
||||
selectedApp?: Application;
|
||||
|
||||
@state()
|
||||
filteredApps: Application[] = [];
|
||||
|
||||
@property()
|
||||
query = getURLParam<string | undefined>("search", undefined);
|
||||
|
||||
fuse: Fuse<Application>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.fuse = new Fuse([], {
|
||||
keys: [
|
||||
{ name: "name", weight: 3 },
|
||||
"slug",
|
||||
"group",
|
||||
{ name: "metaDescription", weight: 0.5 },
|
||||
{ name: "metaPublisher", weight: 0.5 },
|
||||
],
|
||||
findAllMatches: true,
|
||||
includeScore: true,
|
||||
shouldSort: true,
|
||||
ignoreFieldNorm: true,
|
||||
useExtendedSearch: true,
|
||||
threshold: 0.5,
|
||||
});
|
||||
new CoreApi(DEFAULT_CONFIG).coreApplicationsList({}).then((apps) => {
|
||||
this.apps = apps;
|
||||
this.filteredApps = apps.results;
|
||||
this.fuse.setCollection(apps.results);
|
||||
if (!this.query) return;
|
||||
const matchingApps = this.fuse.search(this.query);
|
||||
if (matchingApps.length < 1) return;
|
||||
this.selectedApp = matchingApps[0].item;
|
||||
this.filteredApps = matchingApps.map((a) => a.item);
|
||||
});
|
||||
}
|
||||
|
||||
pageTitle(): string {
|
||||
return t`My Applications`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFDisplay, PFEmptyState, PFPage, PFContent, PFGrid, PFGallery].concat(css`
|
||||
:host,
|
||||
main {
|
||||
padding: 3% 5%;
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.header input {
|
||||
width: 30ch;
|
||||
box-sizing: border-box;
|
||||
border: 0;
|
||||
border-bottom: 1px solid;
|
||||
border-bottom-color: #fd4b2d;
|
||||
background-color: transparent;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.header input:focus {
|
||||
outline: 0;
|
||||
}
|
||||
.pf-c-page__main {
|
||||
overflow: hidden;
|
||||
}
|
||||
.pf-c-page__main-section {
|
||||
background-color: transparent;
|
||||
}
|
||||
.app-group-header {
|
||||
margin-bottom: 1em;
|
||||
margin-top: 1.2em;
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
renderEmptyState(): TemplateResult {
|
||||
return html` <div class="pf-c-empty-state pf-m-full-height">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">${t`No Applications available.`}</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
${t`Either no applications are defined, or you don't have access to any.`}
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
filterApps(apps: Application[]): Application[] {
|
||||
return apps.filter((app) => {
|
||||
if (app.launchUrl && app.launchUrl !== "") {
|
||||
// If the launch URL is a full URL, only show with http or https
|
||||
if (app.launchUrl.indexOf("://") !== -1) {
|
||||
return app.launchUrl.startsWith("http");
|
||||
}
|
||||
// If the URL doesn't include a protocol, assume its a relative path
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
getApps(): [string, Application[]][] {
|
||||
return groupBy(this.filterApps(this.filteredApps), (app) => app.group || "");
|
||||
}
|
||||
|
||||
renderApps(): TemplateResult {
|
||||
let groupClass = "";
|
||||
let groupGrid = "";
|
||||
const uiConfig = rootInterface()?.uiConfig;
|
||||
switch (uiConfig?.layout.type) {
|
||||
case LayoutType.row:
|
||||
groupClass = "pf-m-12-col";
|
||||
groupGrid =
|
||||
"pf-m-all-6-col-on-sm pf-m-all-4-col-on-md pf-m-all-5-col-on-lg pf-m-all-2-col-on-xl";
|
||||
break;
|
||||
case LayoutType.column_2:
|
||||
groupClass = "pf-m-6-col";
|
||||
groupGrid =
|
||||
"pf-m-all-12-col-on-sm pf-m-all-12-col-on-md pf-m-all-4-col-on-lg pf-m-all-4-col-on-xl";
|
||||
break;
|
||||
case LayoutType.column_3:
|
||||
groupClass = "pf-m-4-col";
|
||||
groupGrid =
|
||||
"pf-m-all-12-col-on-sm pf-m-all-12-col-on-md pf-m-all-6-col-on-lg pf-m-all-6-col-on-xl";
|
||||
break;
|
||||
}
|
||||
return html`<div class="pf-l-grid pf-m-gutter">
|
||||
${this.getApps().map(([group, apps]) => {
|
||||
return html`<div class="pf-l-grid__item ${groupClass}">
|
||||
<div class="pf-c-content app-group-header">
|
||||
<h2>${group}</h2>
|
||||
</div>
|
||||
<div class="pf-l-grid pf-m-gutter ${groupGrid}">
|
||||
${apps.map((app) => {
|
||||
return html`<ak-library-app
|
||||
class="pf-l-grid__item"
|
||||
.application=${app}
|
||||
background=${ifDefined(uiConfig?.theme.cardBackground)}
|
||||
?selected=${app.slug === this.selectedApp?.slug}
|
||||
></ak-library-app>`;
|
||||
})}
|
||||
</div>
|
||||
</div> `;
|
||||
})}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
resetSearch(): void {
|
||||
const searchInput = this.shadowRoot?.querySelector("input");
|
||||
if (searchInput) {
|
||||
searchInput.value = "";
|
||||
}
|
||||
this.query = "";
|
||||
updateURLParams({
|
||||
search: this.query,
|
||||
});
|
||||
this.selectedApp = undefined;
|
||||
this.filteredApps = this.apps?.results || [];
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
|
||||
<div class="pf-c-content header">
|
||||
<h1>${t`My applications`}</h1>
|
||||
${rootInterface()?.uiConfig?.enabledFeatures.search
|
||||
? html`<input
|
||||
@input=${(ev: InputEvent) => {
|
||||
this.query = (ev.target as HTMLInputElement).value;
|
||||
if (this.query === "") {
|
||||
return this.resetSearch();
|
||||
}
|
||||
updateURLParams({
|
||||
search: this.query,
|
||||
});
|
||||
const apps = this.fuse.search(this.query);
|
||||
if (apps.length < 1) return;
|
||||
this.selectedApp = apps[0].item;
|
||||
this.filteredApps = apps.map((a) => a.item);
|
||||
}}
|
||||
@keydown=${(ev: KeyboardEvent) => {
|
||||
if (ev.key === "Enter" && this.selectedApp?.launchUrl) {
|
||||
window.location.assign(this.selectedApp.launchUrl);
|
||||
} else if (ev.key === "Escape") {
|
||||
this.resetSearch();
|
||||
}
|
||||
}}
|
||||
type="text"
|
||||
class="pf-u-display-none pf-u-display-block-on-md"
|
||||
autofocus
|
||||
placeholder=${t`Search...`}
|
||||
value=${ifDefined(this.query)}
|
||||
/>`
|
||||
: html``}
|
||||
</div>
|
||||
<section class="pf-c-page__main-section">
|
||||
${loading(
|
||||
this.apps,
|
||||
html`${this.filterApps(this.filteredApps).length > 0
|
||||
? this.renderApps()
|
||||
: this.renderEmptyState()}`,
|
||||
)}
|
||||
</section>
|
||||
</main>`;
|
||||
}
|
||||
}
|
77
web/src/user/LibraryPage/ApplicationEmptyState.ts
Normal file
77
web/src/user/LibraryPage/ApplicationEmptyState.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import { docLink } from "@goauthentik/common/global";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import { paramURL } from "@goauthentik/elements/router/RouterOutlet";
|
||||
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import PFSpacing from "@patternfly/patternfly/utilities/Spacing/spacing.css";
|
||||
|
||||
/**
|
||||
* Library Page Application List Empty
|
||||
*
|
||||
* Display a message if there are no applications defined in the current instance. If the user is an
|
||||
* administrator, provide a link to the "Create a new application" page.
|
||||
*/
|
||||
|
||||
const styles = [
|
||||
PFBase,
|
||||
PFEmptyState,
|
||||
PFButton,
|
||||
PFContent,
|
||||
PFSpacing,
|
||||
css`
|
||||
.cta {
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@customElement("ak-library-application-empty-list")
|
||||
export class LibraryPageApplicationEmptyList extends AKElement {
|
||||
static styles = styles;
|
||||
|
||||
@property({ attribute: "isadmin", type: Boolean })
|
||||
isAdmin = false;
|
||||
|
||||
renderNewAppButton() {
|
||||
const href = paramURL("/core/applications", {
|
||||
createForm: true,
|
||||
});
|
||||
return html`
|
||||
<div class="pf-u-pt-lg">
|
||||
<a
|
||||
aria-disabled="false"
|
||||
class="cta pf-c-button pf-m-secondary"
|
||||
href="/if/admin/${href}"
|
||||
>${t`Create a new application`}</a
|
||||
>
|
||||
</div>
|
||||
<div class="pf-c-empty-state__body">
|
||||
<a href="${docLink("/docs/applications")}" target="_blank"
|
||||
>${t`Refer to documentation`}</a
|
||||
>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html` <div class="pf-c-empty-state pf-m-full-height">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">${t`No Applications available.`}</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
${t`Either no applications are defined, or you don’t have access to any.`}
|
||||
</div>
|
||||
${this.isAdmin ? this.renderNewAppButton() : html``}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
}
|
95
web/src/user/LibraryPage/ApplicationList.ts
Normal file
95
web/src/user/LibraryPage/ApplicationList.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import { LayoutType } from "@goauthentik/common/ui/config";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
|
||||
import { css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";
|
||||
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import type { Application } from "@goauthentik/api";
|
||||
|
||||
import type { AppGroupEntry, AppGroupList } from "./types";
|
||||
|
||||
type Pair = [string, string];
|
||||
|
||||
// prettier-ignore
|
||||
const LAYOUTS = new Map<string, [string, string]>([
|
||||
[
|
||||
"row",
|
||||
["pf-m-12-col", "pf-m-all-6-col-on-sm pf-m-all-4-col-on-md pf-m-all-5-col-on-lg pf-m-all-2-col-on-xl"]],
|
||||
[
|
||||
"2-column",
|
||||
["pf-m-6-col", "pf-m-all-12-col-on-sm pf-m-all-12-col-on-md pf-m-all-4-col-on-lg pf-m-all-4-col-on-xl"],
|
||||
],
|
||||
[
|
||||
"3-column",
|
||||
["pf-m-4-col", "pf-m-all-12-col-on-sm pf-m-all-12-col-on-md pf-m-all-6-col-on-lg pf-m-all-6-col-on-xl"],
|
||||
],
|
||||
]);
|
||||
|
||||
const styles = [
|
||||
PFBase,
|
||||
PFEmptyState,
|
||||
PFContent,
|
||||
PFGrid,
|
||||
css`
|
||||
.app-group-header {
|
||||
margin-bottom: 1em;
|
||||
margin-top: 1.2em;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@customElement("ak-library-application-list")
|
||||
export class LibraryPageApplicationList extends AKElement {
|
||||
static styles = styles;
|
||||
|
||||
@property({ attribute: true })
|
||||
layout = "row" as LayoutType;
|
||||
|
||||
@property({ attribute: true })
|
||||
background: string | undefined = undefined;
|
||||
|
||||
@property({ attribute: true })
|
||||
selected = "";
|
||||
|
||||
@property()
|
||||
apps: AppGroupList = [];
|
||||
|
||||
get currentLayout(): Pair {
|
||||
const layout = LAYOUTS.get(this.layout);
|
||||
if (!layout) {
|
||||
console.warn(`Unrecognized layout: ${this.layout || "-undefined-"}`);
|
||||
return LAYOUTS.get("row") as Pair;
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
render() {
|
||||
const [groupClass, groupGrid] = this.currentLayout;
|
||||
|
||||
return html`<div class="pf-l-grid pf-m-gutter">
|
||||
${this.apps.map(([group, apps]: AppGroupEntry) => {
|
||||
return html`<div class="pf-l-grid__item ${groupClass}">
|
||||
<div class="pf-c-content app-group-header">
|
||||
<h2>${group}</h2>
|
||||
</div>
|
||||
<div class="pf-l-grid pf-m-gutter ${groupGrid}">
|
||||
${apps.map((app: Application) => {
|
||||
return html`<ak-library-app
|
||||
class="pf-l-grid__item"
|
||||
.application=${app}
|
||||
background=${ifDefined(this.background)}
|
||||
?selected=${app.slug === this.selected}
|
||||
></ak-library-app>`;
|
||||
})}
|
||||
</div>
|
||||
</div> `;
|
||||
})}
|
||||
</div>`;
|
||||
}
|
||||
}
|
139
web/src/user/LibraryPage/ApplicationSearch.ts
Normal file
139
web/src/user/LibraryPage/ApplicationSearch.ts
Normal file
@ -0,0 +1,139 @@
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import { getURLParam, updateURLParams } from "@goauthentik/elements/router/RouteMatch";
|
||||
import Fuse from "fuse.js";
|
||||
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { css, html } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
|
||||
|
||||
import type { Application } from "@goauthentik/api";
|
||||
|
||||
import { SEARCH_ITEM_SELECTED, SEARCH_UPDATED } from "./constants";
|
||||
import { customEvent } from "./helpers";
|
||||
|
||||
@customElement("ak-library-list-search")
|
||||
export class LibraryPageApplicationList extends AKElement {
|
||||
static styles = [
|
||||
PFBase,
|
||||
PFDisplay,
|
||||
css`
|
||||
input {
|
||||
width: 30ch;
|
||||
box-sizing: border-box;
|
||||
border: 0;
|
||||
border-bottom: 1px solid;
|
||||
border-bottom-color: var(--ak-accent);
|
||||
background-color: transparent;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
input:focus {
|
||||
outline: 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@property()
|
||||
apps: Application[] = [];
|
||||
|
||||
@property()
|
||||
query = getURLParam<string | undefined>("search", undefined);
|
||||
|
||||
@query("input")
|
||||
searchInput?: HTMLInputElement;
|
||||
|
||||
fuse: Fuse<Application>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.fuse = new Fuse([], {
|
||||
keys: [
|
||||
{ name: "name", weight: 3 },
|
||||
"slug",
|
||||
"group",
|
||||
{ name: "metaDescription", weight: 0.5 },
|
||||
{ name: "metaPublisher", weight: 0.5 },
|
||||
],
|
||||
findAllMatches: true,
|
||||
includeScore: true,
|
||||
shouldSort: true,
|
||||
ignoreFieldNorm: true,
|
||||
useExtendedSearch: true,
|
||||
threshold: 0.5,
|
||||
});
|
||||
}
|
||||
|
||||
onSelected(apps: Fuse.FuseResult<Application>[]) {
|
||||
this.dispatchEvent(
|
||||
customEvent(SEARCH_UPDATED, {
|
||||
apps: apps.map((app) => app.item),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.fuse.setCollection(this.apps);
|
||||
if (!this.query) {
|
||||
return;
|
||||
}
|
||||
const matchingApps = this.fuse.search(this.query);
|
||||
if (matchingApps.length < 1) {
|
||||
return;
|
||||
}
|
||||
this.onSelected(matchingApps);
|
||||
}
|
||||
|
||||
resetSearch(): void {
|
||||
if (this.searchInput) {
|
||||
this.searchInput.value = "";
|
||||
}
|
||||
this.query = "";
|
||||
updateURLParams({
|
||||
search: this.query,
|
||||
});
|
||||
this.onSelected([]);
|
||||
}
|
||||
|
||||
onInput(ev: InputEvent) {
|
||||
this.query = (ev.target as HTMLInputElement).value;
|
||||
if (this.query === "") {
|
||||
return this.resetSearch();
|
||||
}
|
||||
updateURLParams({
|
||||
search: this.query,
|
||||
});
|
||||
const apps = this.fuse.search(this.query);
|
||||
if (apps.length < 1) return;
|
||||
this.onSelected(apps);
|
||||
}
|
||||
|
||||
onKeyDown(ev: KeyboardEvent) {
|
||||
switch (ev.key) {
|
||||
case "Escape": {
|
||||
this.resetSearch();
|
||||
return;
|
||||
}
|
||||
case "Enter": {
|
||||
this.dispatchEvent(customEvent(SEARCH_ITEM_SELECTED));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<input
|
||||
@input=${this.onInput}
|
||||
@keydown=${this.onKeyDown}
|
||||
type="text"
|
||||
class="pf-u-display-none pf-u-display-block-on-md"
|
||||
autofocus
|
||||
placeholder=${t`Search...`}
|
||||
value=${ifDefined(this.query)}
|
||||
/>`;
|
||||
}
|
||||
}
|
96
web/src/user/LibraryPage/LibraryPage.ts
Normal file
96
web/src/user/LibraryPage/LibraryPage.ts
Normal file
@ -0,0 +1,96 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { me } from "@goauthentik/common/users";
|
||||
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/EmptyState";
|
||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
import { Application, CoreApi } from "@goauthentik/api";
|
||||
|
||||
import "./LibraryPageImpl";
|
||||
import type { PageUIConfig } from "./types";
|
||||
|
||||
/**
|
||||
* List of Applications available
|
||||
*
|
||||
* Properties:
|
||||
* apps: a list of the applications available to the user.
|
||||
*
|
||||
* Aggregates two functions:
|
||||
* - Display the list of applications available to the user
|
||||
* - Filter that list using the search bar
|
||||
*
|
||||
*/
|
||||
|
||||
@customElement("ak-library")
|
||||
export class LibraryPage extends AKElement {
|
||||
@state()
|
||||
ready = false;
|
||||
|
||||
@state()
|
||||
isAdmin = false;
|
||||
|
||||
@state()
|
||||
apps!: PaginatedResponse<Application>;
|
||||
|
||||
@state()
|
||||
uiConfig: PageUIConfig;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const applicationListFetch = new CoreApi(DEFAULT_CONFIG).coreApplicationsList({});
|
||||
const meFetch = me();
|
||||
const uiConfig = rootInterface()?.uiConfig;
|
||||
if (!uiConfig) {
|
||||
throw new Error("Could not retrieve uiConfig. Reason: unknown. Check logs.");
|
||||
}
|
||||
|
||||
this.uiConfig = {
|
||||
layout: uiConfig.layout.type,
|
||||
background: uiConfig.theme.cardBackground,
|
||||
searchEnabled: uiConfig.enabledFeatures.search,
|
||||
};
|
||||
|
||||
Promise.allSettled([applicationListFetch, meFetch]).then(
|
||||
([applicationListStatus, meStatus]) => {
|
||||
if (meStatus.status === "rejected") {
|
||||
throw new Error(
|
||||
`Could not determine status of user. Reason: ${meStatus.reason}`,
|
||||
);
|
||||
}
|
||||
if (applicationListStatus.status === "rejected") {
|
||||
throw new Error(
|
||||
`Could not retrieve list of applications. Reason: ${applicationListStatus.reason}`,
|
||||
);
|
||||
}
|
||||
this.isAdmin = meStatus.value.user.isSuperuser;
|
||||
this.apps = applicationListStatus.value;
|
||||
this.ready = true;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pageTitle(): string {
|
||||
return t`My Applications`;
|
||||
}
|
||||
|
||||
loading() {
|
||||
return html`<ak-empty-state ?loading="${true}" header=${t`Loading`}> </ak-empty-state>`;
|
||||
}
|
||||
|
||||
running() {
|
||||
return html`<ak-library-impl
|
||||
?isadmin=${this.isAdmin}
|
||||
.apps=${this.apps}
|
||||
.uiConfig=${this.uiConfig}
|
||||
></ak-library-impl>`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.ready ? this.running() : this.loading();
|
||||
}
|
||||
}
|
39
web/src/user/LibraryPage/LibraryPageImpl.css.ts
Normal file
39
web/src/user/LibraryPage/LibraryPageImpl.css.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { css } from "lit";
|
||||
|
||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";
|
||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
|
||||
|
||||
export const styles = [PFBase, PFDisplay, PFEmptyState, PFPage, PFContent].concat(css`
|
||||
:host {
|
||||
display: block;
|
||||
padding: 3% 5%;
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.header input {
|
||||
width: 30ch;
|
||||
box-sizing: border-box;
|
||||
border: 0;
|
||||
border-bottom: 1px solid;
|
||||
border-bottom-color: #fd4b2d;
|
||||
background-color: transparent;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.header input:focus {
|
||||
outline: 0;
|
||||
}
|
||||
.pf-c-page__main {
|
||||
overflow: hidden;
|
||||
}
|
||||
.pf-c-page__main-section {
|
||||
background-color: transparent;
|
||||
}
|
||||
`);
|
||||
|
||||
export default styles;
|
153
web/src/user/LibraryPage/LibraryPageImpl.ts
Normal file
153
web/src/user/LibraryPage/LibraryPageImpl.ts
Normal file
@ -0,0 +1,153 @@
|
||||
import { groupBy } from "@goauthentik/common/utils";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/EmptyState";
|
||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||
import "@goauthentik/user/LibraryApplication";
|
||||
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import styles from "./LibraryPageImpl.css";
|
||||
|
||||
import type { Application } from "@goauthentik/api";
|
||||
|
||||
import "./ApplicationEmptyState";
|
||||
import "./ApplicationList";
|
||||
import "./ApplicationSearch";
|
||||
import { appHasLaunchUrl } from "./LibraryPageImpl.utils";
|
||||
import { SEARCH_ITEM_SELECTED, SEARCH_UPDATED } from "./constants";
|
||||
import { isCustomEvent, loading } from "./helpers";
|
||||
import type { AppGroupList, PageUIConfig } from "./types";
|
||||
|
||||
/**
|
||||
* List of Applications available
|
||||
*
|
||||
* Properties:
|
||||
* apps: a list of the applications available to the user.
|
||||
*
|
||||
* Aggregates two functions:
|
||||
* - Display the list of applications available to the user
|
||||
* - Filter that list using the search bar
|
||||
*
|
||||
*/
|
||||
|
||||
@customElement("ak-library-impl")
|
||||
export class LibraryPage extends AKElement {
|
||||
static styles = styles;
|
||||
|
||||
@property()
|
||||
apps!: PaginatedResponse<Application>;
|
||||
|
||||
@property({ attribute: "isadmin", type: Boolean })
|
||||
isAdmin = false;
|
||||
|
||||
@property()
|
||||
uiConfig!: PageUIConfig;
|
||||
|
||||
@state()
|
||||
selectedApp?: Application;
|
||||
|
||||
@state()
|
||||
filteredApps: Application[] = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.searchUpdated = this.searchUpdated.bind(this);
|
||||
this.launchRequest = this.launchRequest.bind(this);
|
||||
}
|
||||
|
||||
pageTitle(): string {
|
||||
return t`My Applications`;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.filteredApps = this.apps?.results;
|
||||
if (this.filteredApps === undefined) {
|
||||
throw new Error(
|
||||
"Application.results should never be undefined when passed to the Library Page.",
|
||||
);
|
||||
}
|
||||
this.addEventListener(SEARCH_UPDATED, this.searchUpdated);
|
||||
this.addEventListener(SEARCH_ITEM_SELECTED, this.launchRequest);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this.removeEventListener(SEARCH_UPDATED, this.searchUpdated);
|
||||
this.removeEventListener(SEARCH_ITEM_SELECTED, this.launchRequest);
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
searchUpdated(event: Event) {
|
||||
if (!isCustomEvent(event)) {
|
||||
throw new Error("ak-library-search-updated must send a custom event.");
|
||||
}
|
||||
event.stopPropagation();
|
||||
const apps = event.detail.apps;
|
||||
this.selectedApp = undefined;
|
||||
this.filteredApps = this.apps.results;
|
||||
if (apps.length > 0) {
|
||||
this.selectedApp = apps[0];
|
||||
this.filteredApps = event.detail.apps;
|
||||
}
|
||||
}
|
||||
|
||||
launchRequest(event: Event) {
|
||||
if (!isCustomEvent(event)) {
|
||||
throw new Error("ak-library-item-selected must send a custom event");
|
||||
}
|
||||
event.stopPropagation();
|
||||
const location = this.selectedApp?.launchUrl;
|
||||
if (location) {
|
||||
window.location.assign(location);
|
||||
}
|
||||
}
|
||||
|
||||
getApps(): AppGroupList {
|
||||
return groupBy(this.filteredApps.filter(appHasLaunchUrl), (app) => app.group || "");
|
||||
}
|
||||
|
||||
renderEmptyState() {
|
||||
return html`<ak-library-application-empty-list
|
||||
?isadmin=${this.isAdmin}
|
||||
></ak-library-application-empty-list>`;
|
||||
}
|
||||
|
||||
renderApps() {
|
||||
const selected = this.selectedApp?.slug;
|
||||
const apps = this.getApps();
|
||||
const layout = this.uiConfig.layout as string;
|
||||
const background = this.uiConfig.background;
|
||||
|
||||
return html`<ak-library-application-list
|
||||
layout="${layout}"
|
||||
background="${ifDefined(background)}"
|
||||
selected="${ifDefined(selected)}"
|
||||
.apps=${apps}
|
||||
></ak-library-application-list>`;
|
||||
}
|
||||
|
||||
renderSearch() {
|
||||
return html`<ak-library-list-search .apps=${this.apps.results}></ak-library-list-search>`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
|
||||
<div class="pf-c-content header">
|
||||
<h1>${t`My applications`}</h1>
|
||||
${this.uiConfig.searchEnabled ? this.renderSearch() : html``}
|
||||
</div>
|
||||
<section class="pf-c-page__main-section">
|
||||
${loading(
|
||||
this.apps,
|
||||
html`${this.filteredApps.find(appHasLaunchUrl)
|
||||
? this.renderApps()
|
||||
: this.renderEmptyState()}`,
|
||||
)}
|
||||
</section>
|
||||
</main>`;
|
||||
}
|
||||
}
|
11
web/src/user/LibraryPage/LibraryPageImpl.utils.ts
Normal file
11
web/src/user/LibraryPage/LibraryPageImpl.utils.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import type { Application } from "@goauthentik/api";
|
||||
|
||||
const isFullUrlRe = new RegExp("://");
|
||||
const isHttpRe = new RegExp("http(s?)://");
|
||||
const isNotFullUrl = (url: string) => !isFullUrlRe.test(url);
|
||||
const isHttp = (url: string) => isHttpRe.test(url);
|
||||
|
||||
export const appHasLaunchUrl = (app: Application) => {
|
||||
const url = app.launchUrl;
|
||||
return !!(typeof url === "string" && url !== "" && (isHttp(url) || isNotFullUrl(url)));
|
||||
};
|
2
web/src/user/LibraryPage/constants.ts
Normal file
2
web/src/user/LibraryPage/constants.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export const SEARCH_UPDATED = "authentik.search-updated";
|
||||
export const SEARCH_ITEM_SELECTED = "authentik.search-item-selected";
|
23
web/src/user/LibraryPage/helpers.ts
Normal file
23
web/src/user/LibraryPage/helpers.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import "@goauthentik/elements/EmptyState";
|
||||
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { html } from "lit";
|
||||
import type { TemplateResult } from "lit";
|
||||
|
||||
export const customEvent = (name: string, details = {}) =>
|
||||
new CustomEvent(name as string, {
|
||||
composed: true,
|
||||
bubbles: true,
|
||||
detail: details,
|
||||
});
|
||||
|
||||
// "Unknown" seems to violate some obscure Typescript rule and doesn't work here, although it
|
||||
// should.
|
||||
//
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const isCustomEvent = (v: any): v is CustomEvent =>
|
||||
v instanceof CustomEvent && "detail" in v;
|
||||
|
||||
export const loading = <T>(v: T, actual: TemplateResult) =>
|
||||
v ? actual : html`<ak-empty-state ?loading="${true}" header=${t`Loading`}> </ak-empty-state>`;
|
12
web/src/user/LibraryPage/types.ts
Normal file
12
web/src/user/LibraryPage/types.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import type { LayoutType } from "@goauthentik/common/ui/config";
|
||||
|
||||
import type { Application } from "@goauthentik/api";
|
||||
|
||||
export type AppGroupEntry = [string, Application[]];
|
||||
export type AppGroupList = AppGroupEntry[];
|
||||
|
||||
export type PageUIConfig = {
|
||||
layout: LayoutType;
|
||||
background?: string;
|
||||
searchEnabled: boolean;
|
||||
};
|
@ -1,5 +1,5 @@
|
||||
import { Route } from "@goauthentik/elements/router/Route";
|
||||
import "@goauthentik/user/LibraryPage";
|
||||
import "@goauthentik/user/LibraryPage/LibraryPage";
|
||||
|
||||
import { html } from "lit";
|
||||
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 5.9 MiB |
@ -0,0 +1,88 @@
|
||||
---
|
||||
title: "Join us for an authentik hackathon, 2023!"
|
||||
slug: 2023-05-25-join-us-for-an-authentik-hackathon
|
||||
authors:
|
||||
- name: Jens Langhammer
|
||||
title: CTO at Authentik Security Inc
|
||||
url: https://github.com/BeryJu
|
||||
image_url: https://github.com/BeryJu.png
|
||||
tags:
|
||||
- blog
|
||||
- hackathon
|
||||
- collaboration
|
||||
- doc sprint
|
||||
- git
|
||||
- GitHub profile
|
||||
hide_table_of_contents: false
|
||||
---
|
||||
|
||||
We are thrilled to announce the first ever Authentik Security hackathon! The event will be online, over the course of a week in summer of 2023. More details about the exact days, registration form, and agenda are coming soon.
|
||||
|
||||
Yes, there will be swag and prizes and accolades, possibly even low-key Git-fame.
|
||||
|
||||
More importantly than Git-fame, a hackathon gives us all (authentik employees and our amazing community) a chance to connect and collaborate and learn from one another as we work with the authentik code base and documentation.
|
||||
|
||||
The summer-time schedule for this first authentik hackathon comes about 9 months after we announced the formation of our new company, Authentik Security, back in November 2022 in the blog “[Next steps for Authentik](https://goauthentik.io/blog/2022-11-02-the-next-step-for-authentik)”. We think that getting together with our incredible community, and our still new-ish development team here at Authentik, is a great next step in our journey!
|
||||
|
||||
<!--truncate-->
|
||||
|
||||

|
||||
|
||||
The magic of an organized hackathon is the ability to explore complex challenges in a collaborative, supportive environment, and really put into action the power of multiple brains. This environment fosters deep learning and stimulates trust and confidence… not to mention the potential for career-long connections and accomplishments.
|
||||
|
||||
For that, among many reasons, we hope you will join us for this first-ever authentik hackathon; come build something new with us and add another notch in your Git profile!
|
||||
|
||||
We’ll share more soon about specific goals, and functional areas of the code areas where we want to focus, though all ideas and input are welcomed.
|
||||
|
||||
## Hackathons: the ever-popular event!
|
||||
|
||||
Hackathons have been around a long time in the software world; an event put on by OpenBSD (a free Unix-like operating system) in 1999 is widely considered to be the first hackathon. This was followed closely by a hackathon put on by Sun Microsystems; their event was focused on engineers developing Java programs to run on the Palm, an internet-connected, handheld “personal digital assistant” (PDA).
|
||||
|
||||
Sponsorship from large tech companies continued to be the norm, but during the first decade of the 2000s the format, purpose, and typical attendees of hackathons evolved, with investors taking note of the incredible innovation and product-creation capabilities of hackathons. By the late 2000s, open source projects were a focus, with the power of the community becoming evident.
|
||||
|
||||
The popularity of hackathons does not seem to be slowing down at all, indeed they seem more prevalent than ever, and have surpassed the point of proving that collaboration in open source benefits all sectors of the software industry. Furthermore, as new developers seeking jobs realize the value of investing in their “contributor profiles” on GitHub and GitLab, and university classes promoting participation in open source projects, joining hackathons is a win-win deal.
|
||||
|
||||
## Behind the scenes
|
||||
|
||||
There’s a lot that goes into running a hackathon; entire companies now focus on doing this work!
|
||||
|
||||
Some fundamentals of a successful hackathon include:
|
||||
|
||||
- having a very clear agenda
|
||||
- abundant over-communication
|
||||
- easy-to-find and easy-to-follow instructions for sign-up and participation
|
||||
- a live chat room where participants can ask questions and share ideas
|
||||
- moderators in the repo to review and merge PRs
|
||||
- daily check-in video conferences
|
||||
|
||||
Beyond these important basics, another important consideration is deciding which issues, features, or challenges to work on during the hackathon. It’s fantastic to gather enthusiastic people to work together, but that energy needs to be focused and guided towards the contributions that will add the most value.
|
||||
|
||||
This focus on ideation (exploring and defining the main themes and ideas for the hackathon) should be one of the first steps of planning any hackathon.
|
||||
|
||||
> “Ideation is a crucial part of the hackathon journey because the primary focus of a hackathon is to enable problem-solving. You aren’t there just to write the best code but first to solve a problem that impacts people.” ([source](https://dev.to/appwrite/the-subtle-art-of-hackathon-ideation-1n99))
|
||||
|
||||
A typical process is to have some teams or individuals working on a mix of new features, others on known bugs, and others on popular enhancements. This provides participants a chance to do what they do best, be that writing new code or digging into debugging work.
|
||||
|
||||
## Don’t forget about the … !
|
||||
|
||||
Hackathons aren’t just about code; there’s also documentation, translations, website pages, and more.
|
||||
|
||||
Documentation is an important part of any software project, plus jumping into the docs is a great way for someone who doesn’t code (or wants a break from coding) to still participate and contribute. Docs Sprints, also known as Docathons, have been around almost as long as hackathons. [Sarah Maddox](https://www.atlassian.com/blog/archives/come_join_us_in_an_atlassian_doc_sprint$) made Doc Sprints fun and famous in the early 2010s, managing to bring people together from across the globe for multi-day, chocolate-fueled sessions. Our own tech writer here at Authentik Security held a one-week [Doc Sprint in Kyiv, Ukraine](http://bhmarks.com/blog/ui-components-doc-sprint-hello-kyiv/) that resulted in a completely restructured book about UI Components.
|
||||
|
||||
For our first authentik hackathon, let’s remember the docs and more; if the work you are doing for the hackathon means that the docs need to be updated, jump into the repo (same repo as the code!), or if you want to focus on the docs and help us improve and clean up our existing content, that would be great too. If you see translations that could be improved, visit our [translation project](https://explore.transifex.com/authentik/authentik/) at Transifex and submit your contributions.
|
||||
|
||||
## Input on the authentik hackathon event?
|
||||
|
||||
We’d love to hear from you all about what type of hackathon you’d like to see us put on!
|
||||
|
||||
Here’s a quick summary of our plans so far; let us know your preferences and ideas.
|
||||
|
||||
We are thinking of a multi-day event, with time for participants to get to know more about the project and have discussions about where we want to take the next set of features.
|
||||
|
||||
Kickoff will be on a Tuesday, where we will go over the agenda and instructions, answer any questions, and select which Issues to work on. Wednesday and Thursday dedicated to working on the PRs. On these working days, we will have a dedicated chat channel open, and a daily “check-in” video conference meeting.
|
||||
|
||||
Friday will be wrap-up, final polishing, and signups for demos Friday afternoon/evening and Saturday. We think having the hackathon extend into Saturday is a good way to give people time on weekend to demo if their week’s schedule is busy, but let us know your thoughts, please.
|
||||
|
||||
And back to the swag and fame… after the demos on Saturday, we’ll either do a real-time vote amongst all participants to select the “most impactful” contributions, or conduct an online vote, with all votes due by the following Tuesday.
|
||||
|
||||
We are looking forward to hearing your thoughts, and to seeing you at the hackathon this summer. Reach out to us at [hackathon@goauthentik.io](mailto:hackathon@goauthentik.io) and join us on our **#hackathon23** [Discord channel](https://discord.com/channels/809154715984199690/1110948434552299673) with any suggestions or questions!
|
@ -26,6 +26,29 @@ For example:
|
||||
intent: api
|
||||
```
|
||||
|
||||
### `authentik_core.user`
|
||||
|
||||
:::info
|
||||
Requires authentik 2023.6
|
||||
:::
|
||||
|
||||
Via the standard API, a user's password can only be set via the separate `/api/v3/core/users/<id>/set_password/` endpoint. In blueprints, the password of a user can be set using the `password` field.
|
||||
|
||||
Keep in mind that if an LDAP Source is configured and the user maps to an LDAP user, this password change will be propagated to the LDAP server.
|
||||
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
# [...]
|
||||
- model: authentik_core.user
|
||||
state: present
|
||||
identifiers:
|
||||
username: test-user
|
||||
attrs:
|
||||
name: test user
|
||||
password: this-should-be-a-long-value
|
||||
```
|
||||
|
||||
### `authentik_core.application`
|
||||
|
||||
:::info
|
||||
|
@ -9,35 +9,53 @@ the documentation is easy to read and uses similar phrasing.
|
||||
|
||||
Requirements:
|
||||
|
||||
- Node 16 (or greater)
|
||||
- Node.js 16 (or greater)
|
||||
|
||||
The documentation site is situated in the `/website` folder of the authentik GitHub repository.
|
||||
|
||||
The site is built using npm, below are some useful make commands:
|
||||
|
||||
- Install: `make website-install` (Needed for any of the other tasks)
|
||||
- Formatting: `make website-lint-fix` or `make website` (Run this before committing)
|
||||
- Live editing: `make website-watch` (For real time viewing of changes)
|
||||
- **Installation**: `make website-install`
|
||||
|
||||
This command is required before running any of the following commands, and after upgrading any dependencies.
|
||||
|
||||
- **Formatting**: `make website` or `make website-lint-fix`
|
||||
|
||||
Run this command before committing, to ensure consistent syntax, clean formatting, and verify links. Note that if the formatting command is not run, the build will fail with an error about linting.
|
||||
|
||||
- **Live editing**: `make website-watch`
|
||||
|
||||
For real time viewing of changes, as you make them.
|
||||
|
||||
:::info
|
||||
Be sure to run the formatter before committing changes.
|
||||
:::
|
||||
|
||||
## General guidelines
|
||||
|
||||
- authentik should always be stylized as `authentik` (with a lower-case a and ending with a k)
|
||||
- Documentation should use American english
|
||||
- Feel free to use Docusaurus-specific features, see [here](https://docusaurus.io/docs/next/markdown-features)
|
||||
- Use abbreviations where it makes sense (for commonly used terms like SAML and OAuth)
|
||||
- Phrasing should never blame the user, and should be subjective, i.e
|
||||
- The product name authentik should always be stylized as `authentik` (with a lower-case "a" and ending with a "k").
|
||||
- Documentation should use American English.
|
||||
- You can use standard [Docusaurus-specific features](https://docusaurus.io/docs/next/markdown-features), which include MDX elements such as tabs and admonitions.
|
||||
- Use abbreviations where it makes sense (for commonly used terms like SAML and OAuth) for common terms. If an abbreciation is less-known, spell it out in parentheses after the first use.
|
||||
- Phrasing should almost always be in present tense and active voice:
|
||||
|
||||
- **DON'T** `You may never click x.`
|
||||
- **DO** `x should never be clicked.`
|
||||
- DON'T: "The Applications page will be loaded."
|
||||
|
||||
- When referring to other objects in authentik, use _cursive_ text, and link to the corresponding documentation if possible.
|
||||
- DO: "The Applications page displays."
|
||||
|
||||
- Phrasing should never blame the user, and should be subjective:
|
||||
|
||||
- DON'T: "Never modify the default file."
|
||||
|
||||
- DO: "We recommend not modifying the default file."
|
||||
|
||||
- When referring to UI text or UI components in authentik, use **bold** text.
|
||||
- When referring to other objects in authentik code or functionality, use _cursive_ text, and link to the corresponding documentation if possible.
|
||||
- When referring to external tools, give an example how to use the tools or explain how the user can use them.
|
||||
- Make sure to add the documentation to add to the sidebar, if adding a new page.
|
||||
- Test how the documentation renders using the Netlify Preview, especially when using Docusaurus-specific features.
|
||||
- Test how the documentation renders using the Netlify Deploy Preview, especially when using Docusaurus-specific features.
|
||||
|
||||
If you find any documentation that doesn't match these guidelines, feel free to either open an Issue or a PR so they can be fixed.
|
||||
If you find any documentation that doesn't match these guidelines, feel free to either open an [Issue](https://github.com/goauthentik/authentik/issues) or a [PR](https://github.com/goauthentik/authentik/pulls) so they can be fixed.
|
||||
|
||||
## Integration guidelines
|
||||
|
||||
@ -45,14 +63,13 @@ These guidelines apply in addition to the ones above.
|
||||
|
||||
See the template in `/website/integrations/_template/service.md`.
|
||||
|
||||
- For placeholders, use angle brackets (`<placeholder-name>`).
|
||||
- For placeholders, use angle brackets and italicize the text inside the brackets, to indicate that it is a variable (`_<placeholder-name>_`).
|
||||
|
||||
Make sure to also define if the placeholder is something the user needs to define, something another system defines, or randomly generated.
|
||||
Make sure to also define if the placeholder is something the user needs to define, is something another system defines, or is randomly generated.
|
||||
|
||||
If you're adding configuration snippets to the documentation, and the snippet is in a language that supports comments,
|
||||
other placeholders may be used, for example comments referencing an earlier step.
|
||||
If you're adding configuration snippets to the documentation, and the snippet is in a language that supports comments, other placeholders may be used, for example comments referencing an earlier step.
|
||||
|
||||
- For placeholder domains, use `authentik.company` and `app-name.company`, where `app-name` is the name of the application you are writing documentation for.
|
||||
- For placeholder domains, use `authentik.company` and `app-name.company`, where `app-name` is the name of the application that you are writing documentation for.
|
||||
- Try to order the documentation in the order that makes it easiest for the user to configure.
|
||||
|
||||
- Make sure to add the service to a fitting category in `/website/sidebarsIntegrations.js`
|
||||
|
@ -2,10 +2,10 @@
|
||||
title: Beta versions
|
||||
---
|
||||
|
||||
You can test upcoming authentik versions by switching to the _next_ images. It is recommended to upgrade to the latest stable release before upgrading to beta images. It is always possible to upgrade from the beta to the next stable release.
|
||||
You can test upcoming authentik versions by switching to the _next_ images. It is recommended to upgrade to the latest stable release before upgrading to Beta images. It is always possible to upgrade from the Beta to the next stable release.
|
||||
|
||||
:::warning
|
||||
Downgrading from the Beta is not supported. It is recommended to take a backup before upgrading, or test beta versions on a separate install.
|
||||
Downgrading from the Beta is not supported. It is recommended to take a backup before upgrading, or test Beta versions on a separate install.
|
||||
:::
|
||||
|
||||
import Tabs from "@theme/Tabs";
|
||||
@ -26,9 +26,9 @@ AUTHENTIK_TAG=gh-next
|
||||
AUTHENTIK_OUTPOSTS__CONTAINER_IMAGE_BASE=ghcr.io/goauthentik/dev-%(type)s:gh-%(build_hash)s
|
||||
```
|
||||
|
||||
The beta image is amd64 only. For arm64 platforms, append `-arm64` to the tag name.
|
||||
The Beta image is amd64 only. For arm64 platforms, append `-arm64` to the tag name (no spaces).
|
||||
|
||||
Afterwards, run the upgrade commands from the latest release notes.
|
||||
Next, run the upgrade commands from the latest [Release Notes](../releases).
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="kubernetes">
|
||||
@ -45,9 +45,9 @@ image:
|
||||
pullPolicy: Always
|
||||
```
|
||||
|
||||
The beta image is amd64 only. For arm64 platforms, append `-arm64` to the tag name.
|
||||
The Beta image is amd64 only. For arm64 platforms, append `-arm64` to the tag name (no spaces).
|
||||
|
||||
Afterwards, run the upgrade commands from the latest release notes.
|
||||
Next, run the upgrade commands from the latest [Release Notes](../releases).
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
@ -118,6 +118,17 @@ image:
|
||||
- web/flows: improve UI for TOTP code input (#5676)
|
||||
- web/flows: update flow background (#5639)
|
||||
|
||||
## Fixed in 2023.5.2
|
||||
|
||||
- blueprints: fix check for file path not being run on worker (#5703)
|
||||
- blueprints: support custom ports for OCI blueprints (#5727)
|
||||
- core: bump coverage from 7.2.5 to 7.2.6 (#5738)
|
||||
- core: make groups field for user optional (#5702)
|
||||
- events: fix ak_create_event using wrong request for event creation (#5731)
|
||||
- lib: add tests for ak_create_event (#5710)
|
||||
- outposts: fix missing radius outpost controller (#5730)
|
||||
- web/user: fix MFA enroll dropdown broken when password stage has no configuration flow (#5744)
|
||||
|
||||
## API Changes
|
||||
|
||||
#### What's Changed
|
||||
|
@ -1,9 +1,7 @@
|
||||
---
|
||||
title: Troubleshooting access problems
|
||||
title: I can't access an application
|
||||
---
|
||||
|
||||
### I get an access denied error when trying to access an application.
|
||||
|
||||
If your user is a superuser, or has the attribute `goauthentik.io/user/debug` set to true (can also be set on a group level):
|
||||
|
||||

|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Troubleshooting Login problems
|
||||
title: I can't log in to authentik
|
||||
---
|
||||
|
||||
In case you can't login anymore, perhaps due to an incorrectly configured stage or a failed flow import, you can create a recovery key.
|
||||
|
@ -10,7 +10,11 @@ If it does, you can run the following command to ensure all permissions exist:
|
||||
|
||||
```
|
||||
docker-compose run --rm worker repair_permissions
|
||||
# Or for kubernetes
|
||||
```
|
||||
|
||||
or, for Kubernetes, run
|
||||
|
||||
```
|
||||
kubectl exec -it deployment/authentik-worker -c authentik -- ak repair_permissions
|
||||
```
|
||||
|
||||
|
@ -1,207 +1,231 @@
|
||||
const fs = require("fs");
|
||||
const fs = require("fs").promises;
|
||||
const sidebar = require("./sidebars.js");
|
||||
|
||||
const releases = sidebar.docs
|
||||
.filter((doc) => doc.link?.slug === "releases")[0]
|
||||
.items.filter((release) => typeof release === "string");
|
||||
|
||||
const footerEmail = fs.readFileSync("src/footer.html", { encoding: "utf-8" });
|
||||
|
||||
/** @type {import('@docusaurus/types').DocusaurusConfig} */
|
||||
module.exports = {
|
||||
title: "authentik",
|
||||
tagline: "Making authentication simple.",
|
||||
url: "https://goauthentik.io",
|
||||
baseUrl: "/",
|
||||
onBrokenLinks: "throw",
|
||||
favicon: "img/icon.png",
|
||||
organizationName: "Authentik Security Inc.",
|
||||
projectName: "authentik",
|
||||
themeConfig: {
|
||||
navbar: {
|
||||
title: "authentik",
|
||||
logo: {
|
||||
alt: "authentik logo",
|
||||
src: "img/icon_left_brand.svg",
|
||||
module.exports = async function () {
|
||||
const remarkGithub = (await import("remark-github")).default;
|
||||
const footerEmail = await fs.readFile("src/footer.html", {
|
||||
encoding: "utf-8",
|
||||
});
|
||||
return {
|
||||
title: "authentik",
|
||||
tagline: "Making authentication simple.",
|
||||
url: "https://goauthentik.io",
|
||||
baseUrl: "/",
|
||||
onBrokenLinks: "throw",
|
||||
favicon: "img/icon.png",
|
||||
organizationName: "Authentik Security Inc.",
|
||||
projectName: "authentik",
|
||||
themeConfig: {
|
||||
navbar: {
|
||||
title: "authentik",
|
||||
logo: {
|
||||
alt: "authentik logo",
|
||||
src: "img/icon_left_brand.svg",
|
||||
},
|
||||
items: [
|
||||
{ to: "blog", label: "Blog", position: "left" },
|
||||
{
|
||||
to: "docs/",
|
||||
label: "Docs",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
to: "integrations/",
|
||||
label: "Integrations",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
to: "developer-docs/",
|
||||
label: "Developer",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
to: "pricing/",
|
||||
label: "Pricing",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
to: "jobs/",
|
||||
label: "Jobs",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
type: "dropdown",
|
||||
label: `Version: ${releases[0].replace(
|
||||
/releases\/\d+\/v/,
|
||||
""
|
||||
)}`,
|
||||
position: "right",
|
||||
items: releases.map((release) => {
|
||||
const version = release.replace(
|
||||
/releases\/\d+\/v/,
|
||||
""
|
||||
);
|
||||
const subdomain = version.replace(".", "-");
|
||||
const label = `Version: ${version}`;
|
||||
return {
|
||||
label: label,
|
||||
href: `https://version-${subdomain}.goauthentik.io`,
|
||||
};
|
||||
}),
|
||||
},
|
||||
{
|
||||
href: "https://github.com/goauthentik/authentik",
|
||||
className: "header-github-link",
|
||||
"aria-label": "GitHub repository",
|
||||
position: "right",
|
||||
},
|
||||
{
|
||||
href: "https://goauthentik.io/discord",
|
||||
className: "header-discord-link",
|
||||
"aria-label": "GitHub repository",
|
||||
position: "right",
|
||||
},
|
||||
],
|
||||
},
|
||||
items: [
|
||||
{ to: "blog", label: "Blog", position: "left" },
|
||||
footer: {
|
||||
links: [
|
||||
{
|
||||
title: "Subscribe to authentik News",
|
||||
items: [
|
||||
{
|
||||
html: footerEmail,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Documentation",
|
||||
items: [
|
||||
{
|
||||
label: "Documentation",
|
||||
to: "docs/",
|
||||
},
|
||||
{
|
||||
label: "Integrations",
|
||||
to: "integrations/",
|
||||
},
|
||||
{
|
||||
label: "Developer Documentation",
|
||||
to: "developer-docs/",
|
||||
},
|
||||
{
|
||||
label: "Installations",
|
||||
to: "docs/installation/",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "More",
|
||||
items: [
|
||||
{
|
||||
label: "GitHub",
|
||||
href: "https://github.com/goauthentik/authentik",
|
||||
},
|
||||
{
|
||||
label: "Discord",
|
||||
href: "https://goauthentik.io/discord",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
copyright: `Copyright © ${new Date().getFullYear()} Authentik Security Inc. Built with Docusaurus.`,
|
||||
},
|
||||
tableOfContents: {
|
||||
maxHeadingLevel: 5,
|
||||
},
|
||||
colorMode: {
|
||||
respectPrefersColorScheme: true,
|
||||
},
|
||||
algolia: {
|
||||
appId: "36ROD0O0FV",
|
||||
apiKey: "727db511300ca9aec5425645bbbddfb5",
|
||||
indexName: "goauthentik",
|
||||
},
|
||||
},
|
||||
presets: [
|
||||
[
|
||||
"@docusaurus/preset-classic",
|
||||
{
|
||||
to: "docs/",
|
||||
label: "Docs",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
to: "integrations/",
|
||||
label: "Integrations",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
to: "developer-docs/",
|
||||
label: "Developer",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
to: "pricing/",
|
||||
label: "Pricing",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
to: "jobs/",
|
||||
label: "Jobs",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
type: "dropdown",
|
||||
label: `Version: ${releases[0].replace(
|
||||
/releases\/\d+\/v/,
|
||||
""
|
||||
)}`,
|
||||
position: "right",
|
||||
items: releases.map((release) => {
|
||||
const version = release.replace(/releases\/\d+\/v/, "");
|
||||
const subdomain = version.replace(".", "-");
|
||||
const label = `Version: ${version}`;
|
||||
return {
|
||||
label: label,
|
||||
href: `https://version-${subdomain}.goauthentik.io`,
|
||||
};
|
||||
}),
|
||||
},
|
||||
{
|
||||
href: "https://github.com/goauthentik/authentik",
|
||||
className: "header-github-link",
|
||||
"aria-label": "GitHub repository",
|
||||
position: "right",
|
||||
},
|
||||
{
|
||||
href: "https://goauthentik.io/discord",
|
||||
className: "header-discord-link",
|
||||
"aria-label": "GitHub repository",
|
||||
position: "right",
|
||||
docs: {
|
||||
id: "docs",
|
||||
sidebarPath: require.resolve("./sidebars.js"),
|
||||
editUrl:
|
||||
"https://github.com/goauthentik/authentik/edit/main/website/",
|
||||
remarkPlugins: [
|
||||
[
|
||||
remarkGithub,
|
||||
{
|
||||
repository: "goauthentik/authentik",
|
||||
// Only replace issues and PR links
|
||||
buildUrl: function (
|
||||
values,
|
||||
defaultBuildUrl
|
||||
) {
|
||||
return values.type === "issue"
|
||||
? defaultBuildUrl(values)
|
||||
: false;
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
theme: {
|
||||
customCss: require.resolve("./src/css/custom.css"),
|
||||
},
|
||||
gtag: {
|
||||
trackingID: "G-9MVR9WZFZH",
|
||||
anonymizeIP: true,
|
||||
},
|
||||
blog: {
|
||||
showReadingTime: true,
|
||||
blogSidebarTitle: "All our posts",
|
||||
blogSidebarCount: "ALL",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
footer: {
|
||||
links: [
|
||||
],
|
||||
plugins: [
|
||||
[
|
||||
"@docusaurus/plugin-content-docs",
|
||||
{
|
||||
title: "Subscribe to authentik News",
|
||||
items: [
|
||||
{
|
||||
html: footerEmail,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Documentation",
|
||||
items: [
|
||||
{
|
||||
label: "Documentation",
|
||||
to: "docs/",
|
||||
},
|
||||
{
|
||||
label: "Integrations",
|
||||
to: "integrations/",
|
||||
},
|
||||
{
|
||||
label: "Developer Documentation",
|
||||
to: "developer-docs/",
|
||||
},
|
||||
{
|
||||
label: "Installations",
|
||||
to: "docs/installation/",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "More",
|
||||
items: [
|
||||
{
|
||||
label: "GitHub",
|
||||
href: "https://github.com/goauthentik/authentik",
|
||||
},
|
||||
{
|
||||
label: "Discord",
|
||||
href: "https://goauthentik.io/discord",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
copyright: `Copyright © ${new Date().getFullYear()} Authentik Security Inc. Built with Docusaurus.`,
|
||||
},
|
||||
tableOfContents: {
|
||||
maxHeadingLevel: 5,
|
||||
},
|
||||
colorMode: {
|
||||
respectPrefersColorScheme: true,
|
||||
},
|
||||
algolia: {
|
||||
appId: "36ROD0O0FV",
|
||||
apiKey: "727db511300ca9aec5425645bbbddfb5",
|
||||
indexName: "goauthentik",
|
||||
},
|
||||
},
|
||||
presets: [
|
||||
[
|
||||
"@docusaurus/preset-classic",
|
||||
{
|
||||
docs: {
|
||||
id: "docs",
|
||||
sidebarPath: require.resolve("./sidebars.js"),
|
||||
id: "docsIntegrations",
|
||||
path: "integrations",
|
||||
routeBasePath: "integrations",
|
||||
sidebarPath: require.resolve("./sidebarsIntegrations.js"),
|
||||
editUrl:
|
||||
"https://github.com/goauthentik/authentik/edit/main/website/",
|
||||
},
|
||||
theme: {
|
||||
customCss: require.resolve("./src/css/custom.css"),
|
||||
],
|
||||
[
|
||||
"@docusaurus/plugin-content-docs",
|
||||
{
|
||||
id: "docsDevelopers",
|
||||
path: "developer-docs",
|
||||
routeBasePath: "developer-docs",
|
||||
sidebarPath: require.resolve("./sidebarsDev.js"),
|
||||
editUrl:
|
||||
"https://github.com/goauthentik/authentik/edit/main/website/",
|
||||
},
|
||||
gtag: {
|
||||
trackingID: "G-9MVR9WZFZH",
|
||||
anonymizeIP: true,
|
||||
},
|
||||
blog: {
|
||||
showReadingTime: true,
|
||||
blogSidebarTitle: "All our posts",
|
||||
blogSidebarCount: "ALL",
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
],
|
||||
plugins: [
|
||||
[
|
||||
"@docusaurus/plugin-content-docs",
|
||||
{
|
||||
id: "docsIntegrations",
|
||||
path: "integrations",
|
||||
routeBasePath: "integrations",
|
||||
sidebarPath: require.resolve("./sidebarsIntegrations.js"),
|
||||
editUrl:
|
||||
"https://github.com/goauthentik/authentik/edit/main/website/",
|
||||
},
|
||||
],
|
||||
[
|
||||
"@docusaurus/plugin-content-docs",
|
||||
{
|
||||
id: "docsDevelopers",
|
||||
path: "developer-docs",
|
||||
routeBasePath: "developer-docs",
|
||||
sidebarPath: require.resolve("./sidebarsDev.js"),
|
||||
editUrl:
|
||||
"https://github.com/goauthentik/authentik/edit/main/website/",
|
||||
},
|
||||
],
|
||||
],
|
||||
markdown: {
|
||||
mermaid: true,
|
||||
},
|
||||
themes: ["@docusaurus/theme-mermaid"],
|
||||
scripts: [
|
||||
{
|
||||
src: "https://goauthentik.io/js/script.js",
|
||||
async: true,
|
||||
"data-domain": "goauthentik.io",
|
||||
markdown: {
|
||||
mermaid: true,
|
||||
},
|
||||
{
|
||||
src: "https://boards.greenhouse.io/embed/job_board/js?for=authentiksecurity",
|
||||
},
|
||||
],
|
||||
themes: ["@docusaurus/theme-mermaid"],
|
||||
scripts: [
|
||||
{
|
||||
src: "https://goauthentik.io/js/script.js",
|
||||
async: true,
|
||||
"data-domain": "goauthentik.io",
|
||||
},
|
||||
{
|
||||
src: "https://boards.greenhouse.io/embed/job_board/js?for=authentiksecurity",
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
@ -1,106 +1,127 @@
|
||||
const mainConfig = require("./docusaurus.config");
|
||||
const config = require("./docusaurus.config");
|
||||
|
||||
module.exports = {
|
||||
title: "authentik",
|
||||
tagline: "Making authentication simple.",
|
||||
url: "https://goauthentik.io",
|
||||
baseUrl: "/if/help/",
|
||||
onBrokenLinks: "throw",
|
||||
favicon: "img/icon.png",
|
||||
organizationName: "BeryJu",
|
||||
projectName: "authentik",
|
||||
themeConfig: {
|
||||
navbar: {
|
||||
title: "authentik",
|
||||
logo: {
|
||||
alt: "authentik logo",
|
||||
src: "img/icon_left_brand.svg",
|
||||
},
|
||||
items: [
|
||||
{
|
||||
to: "docs/",
|
||||
activeBasePath: "docs",
|
||||
label: "Docs",
|
||||
position: "left",
|
||||
module.exports = async function () {
|
||||
const remarkGithub = (await import("remark-github")).default;
|
||||
const mainConfig = await config();
|
||||
return {
|
||||
title: "authentik",
|
||||
tagline: "Making authentication simple.",
|
||||
url: "https://goauthentik.io",
|
||||
baseUrl: "/if/help/",
|
||||
onBrokenLinks: "throw",
|
||||
favicon: "img/icon.png",
|
||||
organizationName: "BeryJu",
|
||||
projectName: "authentik",
|
||||
themeConfig: {
|
||||
navbar: {
|
||||
title: "authentik",
|
||||
logo: {
|
||||
alt: "authentik logo",
|
||||
src: "img/icon_left_brand.svg",
|
||||
},
|
||||
{
|
||||
to: "integrations/",
|
||||
activeBasePath: "integrations",
|
||||
label: "Integrations",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
to: "developer-docs/",
|
||||
activeBasePath: "developer-docs",
|
||||
label: "Developer Docs",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
href: "https://github.com/goauthentik/authentik",
|
||||
label: "GitHub",
|
||||
position: "right",
|
||||
},
|
||||
{
|
||||
href: "https://goauthentik.io/discord",
|
||||
label: "Discord",
|
||||
position: "right",
|
||||
},
|
||||
],
|
||||
},
|
||||
footer: mainConfig.themeConfig.footer,
|
||||
colorMode: mainConfig.themeConfig.colorMode,
|
||||
tableOfContents: mainConfig.themeConfig.tableOfContents,
|
||||
},
|
||||
presets: [
|
||||
[
|
||||
"@docusaurus/preset-classic",
|
||||
{
|
||||
docs: {
|
||||
id: "docs",
|
||||
sidebarPath: require.resolve("./sidebars.js"),
|
||||
editUrl:
|
||||
"https://github.com/goauthentik/authentik/edit/main/website/",
|
||||
},
|
||||
pages: false,
|
||||
theme: {
|
||||
customCss: require.resolve("./src/css/custom.css"),
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
plugins: [
|
||||
[
|
||||
"@docusaurus/plugin-content-docs",
|
||||
{
|
||||
id: "docsIntegrations",
|
||||
path: "integrations",
|
||||
routeBasePath: "integrations",
|
||||
sidebarPath: require.resolve("./sidebarsIntegrations.js"),
|
||||
editUrl:
|
||||
"https://github.com/goauthentik/authentik/edit/main/website/",
|
||||
},
|
||||
],
|
||||
[
|
||||
"@docusaurus/plugin-content-docs",
|
||||
{
|
||||
id: "docsDevelopers",
|
||||
path: "developer-docs",
|
||||
routeBasePath: "developer-docs",
|
||||
sidebarPath: require.resolve("./sidebarsDev.js"),
|
||||
editUrl:
|
||||
"https://github.com/goauthentik/authentik/edit/main/website/",
|
||||
},
|
||||
],
|
||||
[
|
||||
"@docusaurus/plugin-client-redirects",
|
||||
{
|
||||
redirects: [
|
||||
items: [
|
||||
{
|
||||
to: "/docs/",
|
||||
from: ["/"],
|
||||
to: "docs/",
|
||||
activeBasePath: "docs",
|
||||
label: "Docs",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
to: "integrations/",
|
||||
activeBasePath: "integrations",
|
||||
label: "Integrations",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
to: "developer-docs/",
|
||||
activeBasePath: "developer-docs",
|
||||
label: "Developer Docs",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
href: "https://github.com/goauthentik/authentik",
|
||||
label: "GitHub",
|
||||
position: "right",
|
||||
},
|
||||
{
|
||||
href: "https://goauthentik.io/discord",
|
||||
label: "Discord",
|
||||
position: "right",
|
||||
},
|
||||
],
|
||||
},
|
||||
footer: mainConfig.themeConfig.footer,
|
||||
colorMode: mainConfig.themeConfig.colorMode,
|
||||
tableOfContents: mainConfig.themeConfig.tableOfContents,
|
||||
},
|
||||
presets: [
|
||||
[
|
||||
"@docusaurus/preset-classic",
|
||||
{
|
||||
docs: {
|
||||
id: "docs",
|
||||
sidebarPath: require.resolve("./sidebars.js"),
|
||||
editUrl:
|
||||
"https://github.com/goauthentik/authentik/edit/main/website/",
|
||||
remarkPlugins: [
|
||||
[
|
||||
remarkGithub,
|
||||
{
|
||||
repository: "goauthentik/authentik",
|
||||
// Only replace issues and PR links
|
||||
buildUrl: function (
|
||||
values,
|
||||
defaultBuildUrl
|
||||
) {
|
||||
return values.type === "issue"
|
||||
? defaultBuildUrl(values)
|
||||
: false;
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
pages: false,
|
||||
theme: {
|
||||
customCss: require.resolve("./src/css/custom.css"),
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
],
|
||||
plugins: [
|
||||
[
|
||||
"@docusaurus/plugin-content-docs",
|
||||
{
|
||||
id: "docsIntegrations",
|
||||
path: "integrations",
|
||||
routeBasePath: "integrations",
|
||||
sidebarPath: require.resolve("./sidebarsIntegrations.js"),
|
||||
editUrl:
|
||||
"https://github.com/goauthentik/authentik/edit/main/website/",
|
||||
},
|
||||
],
|
||||
[
|
||||
"@docusaurus/plugin-content-docs",
|
||||
{
|
||||
id: "docsDevelopers",
|
||||
path: "developer-docs",
|
||||
routeBasePath: "developer-docs",
|
||||
sidebarPath: require.resolve("./sidebarsDev.js"),
|
||||
editUrl:
|
||||
"https://github.com/goauthentik/authentik/edit/main/website/",
|
||||
},
|
||||
],
|
||||
[
|
||||
"@docusaurus/plugin-client-redirects",
|
||||
{
|
||||
redirects: [
|
||||
{
|
||||
to: "/docs/",
|
||||
from: ["/"],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
};
|
||||
|
@ -26,9 +26,9 @@ The following placeholders will be used:
|
||||
Create an application in authentik and note the slug, as this will be used later. Create a SAML provider with the following parameters:
|
||||
|
||||
- ACS URL: `https://signin.aws.amazon.com/saml`
|
||||
- Audience: `urn:amazon:webservices`
|
||||
- Issuer: `authentik`
|
||||
- Binding: `Post`
|
||||
- Audience: `urn:amazon:webservices`
|
||||
|
||||
You can of course use a custom signing certificate, and adjust durations.
|
||||
|
||||
@ -100,10 +100,10 @@ Additional Preparation:
|
||||
|
||||
In AWS:
|
||||
|
||||
- In AWS navigate to: `IAM Identity Center -> Settings -> Identity Source (tab)`
|
||||
- On the right side click `Actions -> Change identity source`
|
||||
- Select `External Identity Provider`
|
||||
- Under `Service Provider metadata` download the metadata file.
|
||||
- In AWS navigate to: _IAM Identity Center_ -> _Settings_ -> _Identity Source (tab)_
|
||||
- On the right side click _Actions_ -> _Change identity source_
|
||||
- Select _External Identity Provider_
|
||||
- Under _Service Provider metadata_ download the metadata file.
|
||||
|
||||
Now go to your authentik instance, and perform the following steps.
|
||||
|
||||
@ -118,10 +118,10 @@ Now go to your authentik instance, and perform the following steps.
|
||||
|
||||
Now go back to your AWS instance
|
||||
|
||||
- Under `Identity provider metadata` upload both the the `Metadata` file and `Signing Certificate` that authentik gave you.
|
||||
- Click `Next`.
|
||||
- In your settings pane, under the tab `Identity Source`, click `Actions -> Manage Authentication`.
|
||||
- Take note of the `AWS access portal sign-in URL` (this is especially important if you changed it from the default).
|
||||
- Under _Identity provider metadata_ upload both the the _Metadata_ file and _Signing Certificate_ that authentik gave you.
|
||||
- Click _Next_.
|
||||
- In your settings pane, under the tab _Identity Source_, click _Actions_ -> _Manage Authentication_.
|
||||
- Take note of the _AWS access portal sign-in URL_ (this is especially important if you changed it from the default).
|
||||
|
||||
Now go back to your authentik instance.
|
||||
|
||||
@ -141,8 +141,8 @@ Some people may opt TO USE the automatic provisioning feature called SCIM (Syste
|
||||
SCIM allows you to synchronize (part of) your directory to AWS's IAM, saving you the hassle of having to create users by hand.
|
||||
In order to do so, take the following steps in your AWS Identity Center:
|
||||
|
||||
- In your `Settings` pane, locate the `Automatic Provisioning` Info box. Click `Enable`.
|
||||
- AWS will give you an `SCIM Endpoint` and a `Access Token`. Take note of these values.
|
||||
- In your _Settings_ pane, locate the _Automatic Provisioning_ information box. Click _Enable_.
|
||||
- AWS will give you an _SCIM Endpoint_ and a _Access Token_. Take note of these values.
|
||||
|
||||
Go back to your authentik instance
|
||||
|
||||
@ -172,4 +172,4 @@ return {
|
||||
- Under _Backchannel providers_ add the SCIM provider that you created.
|
||||
- Click _Update_
|
||||
|
||||
The SCIM provider should sync automatically whenever you create/alter/remove anything. You can manually sync by going to your SCIM provider and click the _Run sync again_ button. Once the SCIM provider has synced, you should see the users and groups in your AWS IAM center.
|
||||
The SCIM provider syncs automatically whenever you create/update/remove users, groups, or group membership. You can manually sync by going to your SCIM provider and clicking _Run sync again_. After the SCIM provider has synced, you should see the users and groups in your AWS IAM center.
|
||||
|
61
website/integrations/services/google/index.md
Normal file
61
website/integrations/services/google/index.md
Normal file
@ -0,0 +1,61 @@
|
||||
---
|
||||
title: Google Workspace
|
||||
---
|
||||
|
||||
<span class="badge badge--primary">Support level: authentik</span>
|
||||
|
||||
## What is Google Workspace
|
||||
|
||||
From https://en.wikipedia.org/wiki/Google_Workspace
|
||||
|
||||
:::note
|
||||
Google Workspace is a collection of cloud computing, productivity and collaboration tools, software and products developed and marketed by Google.
|
||||
:::
|
||||
|
||||
## Preparation
|
||||
|
||||
The following placeholders will be used:
|
||||
|
||||
- `authentik.company` is the FQDN of the authentik install.
|
||||
- `example.com` is the default E-mail address configured in Google workspace.
|
||||
|
||||
## authentik Configuration
|
||||
|
||||
Create an application in authentik and note the slug, as this will be used later. Set the _Launch URL_ to `https://mail.google.com/a/example.com`.
|
||||
|
||||
Create a SAML provider with the following parameters:
|
||||
|
||||
- ACS URL: `https://www.google.com/a/example.com/acs`
|
||||
- Issuer: `google.com/a/example.com`
|
||||
- Binding: `Post`
|
||||
- Audience: `google.com/a/example.com`
|
||||
|
||||
Under _Advanced protocol settings_, set the option _NameID Property Mapping_ to the default E-mail property mapping called _authentik default SAML Mapping: Email_. Also make sure a _Signing Certificate_ is selected in the same section.
|
||||
|
||||
Copy the values of _SSO URL (Redirect)_ and _SLO URL (Redirect)_ fields from the provider page.
|
||||
|
||||
Click the _Download_ button next to the _Download signing certificate_ label.
|
||||
|
||||
## Google Workspace Configuration
|
||||
|
||||
Log in to the Google Workspace Admin portal by navigating to https://admin.google.com/, and authenticating with a super-admin account.
|
||||
|
||||
Navigate to _Security_ -> _Authentication_ -> _SSO with third-party IdP_.
|
||||
|
||||
Open the _Third-party SSO profile for your organization_ section.
|
||||
|
||||
Check the checkbox _Set up SSO with third-party identity provider_.
|
||||
|
||||
Set the value of _Sign-in page URL_ to the copied _SSO URL (Redirect)_ from above.
|
||||
|
||||
Set the value of _Sign-out page URL_ to the copied _SLO URL (Redirect)_ from above.
|
||||
|
||||
For _Verification certificate_, upload the certificate that you downloaded previously.
|
||||
|
||||
Ensure the option _Use a domain specific issuer_ is enabled.
|
||||
|
||||
## Notes
|
||||
|
||||
Google will not use these SSO settings with super-admins, although they will apply for any other user account. User accounts must already exist in Google workspace when attempting to login with authentik; Google will not create them automatically.
|
||||
|
||||
To verify that the configuration is correct for a super-admin account, navigate to `https://mail.google.com/a/example.com`, which redirects to the configured authentik instance.
|
@ -31,7 +31,7 @@ The following placeholders will be used:
|
||||
- `vcenter.company` is the FQDN of the vCenter server.
|
||||
- `authentik.company` is the FQDN of the authentik install.
|
||||
|
||||
Since vCenter only allows OpenID-Connect in combination with Active Directory, it is recommended to have authentik sync with the same Active Directory.
|
||||
Since vCenter only allows OpenID-Connect in combination with Active Directory/LDAP, it is recommended to have authentik sync with the same Active Directory. You also have the option of connecting to an authentik managed LDAP outpost for user management.
|
||||
|
||||
### Step 1
|
||||
|
||||
@ -43,6 +43,25 @@ return {
|
||||
}
|
||||
```
|
||||
|
||||
If you are using an authentik managed LDAP outpost you can use the following expression in your property mapping. This will correctly return the `groups` claim as a list of LDAP DNs instead of their names.
|
||||
|
||||
```python
|
||||
ldap_base_dn = "DC=ldap,DC=goauthentik,DC=io"
|
||||
groups = []
|
||||
for group in request.user.ak_groups.all():
|
||||
group_dn = f"CN={group.name},dc=groups,{ldap_base_dn}"
|
||||
groups.append(group_dn)
|
||||
return {
|
||||
"name": request.user.name,
|
||||
"email": request.user.email,
|
||||
"given_name": request.user.name,
|
||||
"preferred_username": request.user.username,
|
||||
"nickname": request.user.username,
|
||||
"groups": groups,
|
||||
"domain": "ldap.goauthentik.io"
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2
|
||||
|
||||
:::note
|
||||
@ -56,7 +75,7 @@ Under _Sources_, click _Edit_ and ensure that "authentik default Active Director
|
||||
Under _Providers_, create an OAuth2/OpenID provider with these settings:
|
||||
|
||||
- Redirect URI: `https://vcenter.company/ui/login/oauth2/authcode`
|
||||
- Sub Mode: If your Email address Schema matches your UPN, select "Based on the User's Email...", otherwise select "Based on the User's UPN...".
|
||||
- Sub Mode: If your Email address Schema matches your UPN, select "Based on the User's Email...", otherwise select "Based on the User's UPN...". If you are using authentik's managed LDAP outpost, chose "Based on the User's username"
|
||||
- Scopes: Select the Scope Mapping you've created in Step 1
|
||||
- Signing Key: Select any available key
|
||||
|
||||
|
@ -52,3 +52,105 @@ Save, and you now have Discord as a source.
|
||||
:::note
|
||||
For more details on how-to have the new source display on the Login Page see [here](../general#add-sources-to-default-login-page).
|
||||
:::
|
||||
|
||||
### Checking for membership of a Discord Guild
|
||||
|
||||
:::info
|
||||
Ensure that the Discord OAuth source in 'Federation & Social login' has the additional `guilds` scope added under the 'Protocol settings'.
|
||||
:::
|
||||
|
||||
Create a new 'Expression Policy' with the content below, adjusting the variables where required:
|
||||
|
||||
```python
|
||||
# To get the guild ID number for the parameters, open Discord, go to Settings > Advanced and enable developer mode.
|
||||
# Right-click on the server/guild title and select "Copy ID" to get the guild ID.
|
||||
|
||||
ACCEPTED_GUILD_ID = "123456789123456789"
|
||||
GUILD_NAME_STRING = "The desired server/guild name in the error message."
|
||||
|
||||
# Only change below here if you know what you are doing.
|
||||
|
||||
# Ensure flow is only run during OAuth logins via Discord
|
||||
if context['source'].provider_type != "discord":
|
||||
return True
|
||||
|
||||
# Get the user-source connection object from the context, and get the access token
|
||||
connection = context.get("goauthentik.io/sources/connection")
|
||||
if not connection:
|
||||
return False
|
||||
access_token = connection.access_token
|
||||
|
||||
guilds = requests.get(
|
||||
"https://discord.com/api/users/@me/guilds",
|
||||
headers= {
|
||||
"Authorization": f"Bearer {access_token}",
|
||||
}
|
||||
).json()
|
||||
|
||||
user_matched = any(ACCEPTED_GUILD_ID == g["id"] for g in guilds)
|
||||
if not user_matched:
|
||||
ak_message(f"User is not a member of {GUILD_NAME_STRING}.")
|
||||
return user_matched
|
||||
```
|
||||
|
||||
Now bind this policy to the chosen enrollment and authentication flows for the Discord OAuth source.
|
||||
|
||||
### Checking for membership of a Discord Guild role
|
||||
|
||||
:::info
|
||||
Ensure that the Discord OAuth source in 'Federation & Social login' has the additional `guilds guilds.members.read` scopes added under the 'Protocol settings'.
|
||||
:::
|
||||
|
||||
Create a new 'Expression Policy' with the content below, adjusting the variables where required:
|
||||
|
||||
```python
|
||||
# To get the role and guild ID numbers for the parameters, open Discord, go to Settings > Advanced and
|
||||
# enable developer mode.
|
||||
# Right-click on the server/guild title and select "Copy ID" to get the guild ID.
|
||||
# Right-click on the server/guild title and select server settings > roles, right click on the role and click
|
||||
# "Copy ID" to get the role ID.
|
||||
|
||||
ACCEPTED_ROLE_ID = "123456789123456789"
|
||||
ACCEPTED_GUILD_ID = "123456789123456789"
|
||||
GUILD_NAME_STRING = "The desired server/guild name in the error message."
|
||||
ROLE_NAME_STRING = "The desired role name in the error message."
|
||||
|
||||
# Only change below here if you know what you are doing.
|
||||
GUILD_API_URL = f"https://discord.com/api/users/@me/guilds/{ACCEPTED_GUILD_ID}/member"
|
||||
|
||||
# Ensure flow is only run during OAuth logins via Discord
|
||||
if context['source'].provider_type != "discord":
|
||||
return True
|
||||
|
||||
# Get the user-source connection object from the context, and get the access token
|
||||
connection = context.get("goauthentik.io/sources/connection")
|
||||
if not connection:
|
||||
return False
|
||||
access_token = connection.access_token
|
||||
|
||||
guild_member_object = requests.get(
|
||||
GUILD_API_URL,
|
||||
headers= {
|
||||
"Authorization": f"Bearer {access_token}",
|
||||
}
|
||||
).json()
|
||||
|
||||
# The response for JSON errors is held within guild_member_object['code']
|
||||
# See: https://discord.com/developers/docs/topics/opcodes-and-status-codes#json
|
||||
# If the user isn't in the queried guild, it gives the somewhat misleading code = 10004.
|
||||
if "code" in guild_member_object:
|
||||
if guild_member_object['code'] == 10004:
|
||||
ak_message(f"User is not a member of {GUILD_NAME_STRING}.")
|
||||
else:
|
||||
ak_create_event("discord_error", source=context['source'], code=guild_member_object['code'])
|
||||
ak_message("Discord API error, try again later.")
|
||||
# Policy does not match if there is any error.
|
||||
return False
|
||||
|
||||
user_matched = any(ACCEPTED_ROLE_ID == g for g in guild_member_object["roles"])
|
||||
if not user_matched:
|
||||
ak_message(f"User is not a member of the {ROLE_NAME_STRING} role in {GUILD_NAME_STRING}.")
|
||||
return user_matched
|
||||
```
|
||||
|
||||
Now bind this policy to the chosen enrollment and authentication flows for the Discord OAuth source.
|
||||
|
@ -2,18 +2,18 @@ const gitHubNamespace = "goauthentik";
|
||||
|
||||
exports.handler = async function (event, context) {
|
||||
let repo = "";
|
||||
if (event.path === "/") {
|
||||
repo = "/authentik";
|
||||
} else if (event.path.startsWith("/api")) {
|
||||
repo = "/client-go";
|
||||
if (event.path.startsWith("/api")) {
|
||||
repo = "client-go";
|
||||
} else if (event.path.startsWith("/terraform-provider-authentik")) {
|
||||
repo = "terraform-provider-authentik";
|
||||
} else {
|
||||
repo = event.path;
|
||||
repo = "authentik";
|
||||
}
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
"content-type": "text/html",
|
||||
},
|
||||
body: `<meta name="go-import" content="${event.headers.host}${event.path} git https://github.com/${gitHubNamespace}${repo}">`,
|
||||
body: `<meta name="go-import" content="${event.headers.host}${event.path} git https://github.com/${gitHubNamespace}/${repo}">`,
|
||||
};
|
||||
};
|
||||
|
363
website/package-lock.json
generated
363
website/package-lock.json
generated
@ -15,13 +15,14 @@
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"clsx": "^1.2.1",
|
||||
"disqus-react": "^1.1.5",
|
||||
"postcss": "^8.4.23",
|
||||
"postcss": "^8.4.24",
|
||||
"rapidoc": "^9.3.4",
|
||||
"react": "^17.0.2",
|
||||
"react-before-after-slider-component": "^1.1.8",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-feather": "^2.0.10",
|
||||
"react-toggle": "^4.1.3"
|
||||
"react-toggle": "^4.1.3",
|
||||
"remark-github": "^11.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "2.8.8"
|
||||
@ -8443,6 +8444,57 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-find-and-replace": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.2.tgz",
|
||||
"integrity": "sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^3.0.0",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"unist-util-is": "^5.0.0",
|
||||
"unist-util-visit-parents": "^5.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
|
||||
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-find-and-replace/node_modules/unist-util-is": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz",
|
||||
"integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==",
|
||||
"dependencies": {
|
||||
"@types/unist": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-find-and-replace/node_modules/unist-util-visit-parents": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz",
|
||||
"integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==",
|
||||
"dependencies": {
|
||||
"@types/unist": "^2.0.0",
|
||||
"unist-util-is": "^5.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-to-hast": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz",
|
||||
@ -9316,9 +9368,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.23",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz",
|
||||
"integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==",
|
||||
"version": "8.4.24",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
|
||||
"integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -10664,6 +10716,160 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-github": {
|
||||
"version": "11.2.4",
|
||||
"resolved": "https://registry.npmjs.org/remark-github/-/remark-github-11.2.4.tgz",
|
||||
"integrity": "sha512-GJjWFpwqdrHHhPWqMbb8+lqFLiHQ9pCzUmXmRrhMFXGpYov5n2ljsZzuWgXlfzArfQYkiKIZczA2I8IHYMHqCA==",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^3.0.0",
|
||||
"mdast-util-find-and-replace": "^2.0.0",
|
||||
"mdast-util-to-string": "^3.0.0",
|
||||
"unified": "^10.0.0",
|
||||
"unist-util-visit": "^4.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-github/node_modules/bail": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
|
||||
"integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-github/node_modules/is-plain-obj": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
|
||||
"integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-github/node_modules/mdast-util-to-string": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz",
|
||||
"integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^3.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-github/node_modules/trough": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz",
|
||||
"integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-github/node_modules/unified": {
|
||||
"version": "10.1.2",
|
||||
"resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz",
|
||||
"integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==",
|
||||
"dependencies": {
|
||||
"@types/unist": "^2.0.0",
|
||||
"bail": "^2.0.0",
|
||||
"extend": "^3.0.0",
|
||||
"is-buffer": "^2.0.0",
|
||||
"is-plain-obj": "^4.0.0",
|
||||
"trough": "^2.0.0",
|
||||
"vfile": "^5.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-github/node_modules/unist-util-is": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz",
|
||||
"integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==",
|
||||
"dependencies": {
|
||||
"@types/unist": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-github/node_modules/unist-util-stringify-position": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz",
|
||||
"integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==",
|
||||
"dependencies": {
|
||||
"@types/unist": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-github/node_modules/unist-util-visit": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz",
|
||||
"integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==",
|
||||
"dependencies": {
|
||||
"@types/unist": "^2.0.0",
|
||||
"unist-util-is": "^5.0.0",
|
||||
"unist-util-visit-parents": "^5.1.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-github/node_modules/unist-util-visit-parents": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz",
|
||||
"integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==",
|
||||
"dependencies": {
|
||||
"@types/unist": "^2.0.0",
|
||||
"unist-util-is": "^5.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-github/node_modules/vfile": {
|
||||
"version": "5.3.7",
|
||||
"resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz",
|
||||
"integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==",
|
||||
"dependencies": {
|
||||
"@types/unist": "^2.0.0",
|
||||
"is-buffer": "^2.0.0",
|
||||
"unist-util-stringify-position": "^3.0.0",
|
||||
"vfile-message": "^3.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-github/node_modules/vfile-message": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz",
|
||||
"integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==",
|
||||
"dependencies": {
|
||||
"@types/unist": "^2.0.0",
|
||||
"unist-util-stringify-position": "^3.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-mdx": {
|
||||
"version": "1.6.22",
|
||||
"resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-1.6.22.tgz",
|
||||
@ -19636,6 +19842,41 @@
|
||||
"unist-util-visit": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"mdast-util-find-and-replace": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.2.tgz",
|
||||
"integrity": "sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==",
|
||||
"requires": {
|
||||
"@types/mdast": "^3.0.0",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"unist-util-is": "^5.0.0",
|
||||
"unist-util-visit-parents": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"escape-string-regexp": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
|
||||
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="
|
||||
},
|
||||
"unist-util-is": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz",
|
||||
"integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==",
|
||||
"requires": {
|
||||
"@types/unist": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"unist-util-visit-parents": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz",
|
||||
"integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==",
|
||||
"requires": {
|
||||
"@types/unist": "^2.0.0",
|
||||
"unist-util-is": "^5.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"mdast-util-to-hast": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz",
|
||||
@ -20253,9 +20494,9 @@
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.4.23",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz",
|
||||
"integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==",
|
||||
"version": "8.4.24",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
|
||||
"integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==",
|
||||
"requires": {
|
||||
"nanoid": "^3.3.6",
|
||||
"picocolors": "^1.0.0",
|
||||
@ -21163,6 +21404,112 @@
|
||||
"resolved": "https://registry.npmjs.org/remark-footnotes/-/remark-footnotes-2.0.0.tgz",
|
||||
"integrity": "sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ=="
|
||||
},
|
||||
"remark-github": {
|
||||
"version": "11.2.4",
|
||||
"resolved": "https://registry.npmjs.org/remark-github/-/remark-github-11.2.4.tgz",
|
||||
"integrity": "sha512-GJjWFpwqdrHHhPWqMbb8+lqFLiHQ9pCzUmXmRrhMFXGpYov5n2ljsZzuWgXlfzArfQYkiKIZczA2I8IHYMHqCA==",
|
||||
"requires": {
|
||||
"@types/mdast": "^3.0.0",
|
||||
"mdast-util-find-and-replace": "^2.0.0",
|
||||
"mdast-util-to-string": "^3.0.0",
|
||||
"unified": "^10.0.0",
|
||||
"unist-util-visit": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"bail": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
|
||||
"integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="
|
||||
},
|
||||
"is-plain-obj": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
|
||||
"integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="
|
||||
},
|
||||
"mdast-util-to-string": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz",
|
||||
"integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==",
|
||||
"requires": {
|
||||
"@types/mdast": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"trough": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz",
|
||||
"integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g=="
|
||||
},
|
||||
"unified": {
|
||||
"version": "10.1.2",
|
||||
"resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz",
|
||||
"integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==",
|
||||
"requires": {
|
||||
"@types/unist": "^2.0.0",
|
||||
"bail": "^2.0.0",
|
||||
"extend": "^3.0.0",
|
||||
"is-buffer": "^2.0.0",
|
||||
"is-plain-obj": "^4.0.0",
|
||||
"trough": "^2.0.0",
|
||||
"vfile": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"unist-util-is": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz",
|
||||
"integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==",
|
||||
"requires": {
|
||||
"@types/unist": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"unist-util-stringify-position": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz",
|
||||
"integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==",
|
||||
"requires": {
|
||||
"@types/unist": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"unist-util-visit": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz",
|
||||
"integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==",
|
||||
"requires": {
|
||||
"@types/unist": "^2.0.0",
|
||||
"unist-util-is": "^5.0.0",
|
||||
"unist-util-visit-parents": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"unist-util-visit-parents": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz",
|
||||
"integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==",
|
||||
"requires": {
|
||||
"@types/unist": "^2.0.0",
|
||||
"unist-util-is": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"vfile": {
|
||||
"version": "5.3.7",
|
||||
"resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz",
|
||||
"integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==",
|
||||
"requires": {
|
||||
"@types/unist": "^2.0.0",
|
||||
"is-buffer": "^2.0.0",
|
||||
"unist-util-stringify-position": "^3.0.0",
|
||||
"vfile-message": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"vfile-message": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz",
|
||||
"integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==",
|
||||
"requires": {
|
||||
"@types/unist": "^2.0.0",
|
||||
"unist-util-stringify-position": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"remark-mdx": {
|
||||
"version": "1.6.22",
|
||||
"resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-1.6.22.tgz",
|
||||
|
@ -22,13 +22,14 @@
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"clsx": "^1.2.1",
|
||||
"disqus-react": "^1.1.5",
|
||||
"postcss": "^8.4.23",
|
||||
"postcss": "^8.4.24",
|
||||
"rapidoc": "^9.3.4",
|
||||
"react": "^17.0.2",
|
||||
"react-before-after-slider-component": "^1.1.8",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-feather": "^2.0.10",
|
||||
"react-toggle": "^4.1.3"
|
||||
"react-toggle": "^4.1.3",
|
||||
"remark-github": "^11.2.4"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
@ -302,12 +302,12 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
"troubleshooting/access",
|
||||
"troubleshooting/csrf",
|
||||
"troubleshooting/emails",
|
||||
"troubleshooting/login",
|
||||
"troubleshooting/image_upload",
|
||||
"troubleshooting/missing_permission",
|
||||
"troubleshooting/missing_admin_group",
|
||||
"troubleshooting/csrf",
|
||||
"troubleshooting/emails",
|
||||
"troubleshooting/ldap_source",
|
||||
],
|
||||
},
|
||||
|
@ -36,6 +36,7 @@ module.exports = {
|
||||
label: "Cloud Providers",
|
||||
items: [
|
||||
"services/aws/index",
|
||||
"services/google/index",
|
||||
"services/hashicorp-cloud/index",
|
||||
"services/oracle-cloud/index",
|
||||
],
|
||||
|
@ -8,7 +8,7 @@
|
||||
#mc_embed_signup {
|
||||
clear: left;
|
||||
font: 14px Helvetica, Arial, sans-serif;
|
||||
width: 600px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#mc_embed_signup .helper_text {
|
||||
|
Reference in New Issue
Block a user