Merge branch 'dev' into web/sidebar-with-live-content-3
* dev: (72 commits) web/flows: show logo in card (#7824) blueprints: improve file change handler (#7813) web/user: fix search not updating app (#7825) web: bump the storybook group in /web with 5 updates (#7819) core: compile backend translations (#7827) translate: Updates for file locale/en/LC_MESSAGES/django.po in de (#7812) core: bump github.com/go-openapi/strfmt from 0.21.8 to 0.21.9 (#7814) ci: bump actions/stale from 8 to 9 (#7815) web: bump the wdio group in /tests/wdio with 1 update (#7816) translate: Updates for file web/xliff/en.xlf in zh_CN (#7820) web: bump the sentry group in /web with 2 updates (#7817) web: bump vite-tsconfig-paths from 4.2.1 to 4.2.2 in /web (#7818) translate: Updates for file web/xliff/en.xlf in zh-Hans (#7821) translate: Updates for file locale/en/LC_MESSAGES/django.po in zh-Hans (#7822) translate: Updates for file locale/en/LC_MESSAGES/django.po in zh_CN (#7823) web: bump typescript from 5.3.2 to 5.3.3 in /web (#7806) website: bump typescript from 5.3.2 to 5.3.3 in /website (#7807) web: bump typescript from 5.3.2 to 5.3.3 in /tests/wdio (#7808) core: bump goauthentik.io/api/v3 from 3.2023104.1 to 3.2023104.2 (#7809) ci: bump actions/setup-go from 4 to 5 ...
This commit is contained in:
commit
2a11356961
|
@ -16,7 +16,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v4
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: "go.mod"
|
go-version-file: "go.mod"
|
||||||
- name: Prepare and generate API
|
- name: Prepare and generate API
|
||||||
|
@ -37,7 +37,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v4
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: "go.mod"
|
go-version-file: "go.mod"
|
||||||
- name: Setup authentik env
|
- name: Setup authentik env
|
||||||
|
@ -125,7 +125,7 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
- uses: actions/setup-go@v4
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: "go.mod"
|
go-version-file: "go.mod"
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
|
|
|
@ -67,7 +67,7 @@ jobs:
|
||||||
- radius
|
- radius
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v4
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: "go.mod"
|
go-version-file: "go.mod"
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
|
@ -126,7 +126,7 @@ jobs:
|
||||||
goarch: [amd64, arm64]
|
goarch: [amd64, arm64]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v4
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: "go.mod"
|
go-version-file: "go.mod"
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
|
|
|
@ -18,7 +18,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
app_id: ${{ secrets.GH_APP_ID }}
|
app_id: ${{ secrets.GH_APP_ID }}
|
||||||
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||||
- uses: actions/stale@v8
|
- uses: actions/stale@v9
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ steps.generate_token.outputs.token }}
|
repo-token: ${{ steps.generate_token.outputs.token }}
|
||||||
days-before-stale: 60
|
days-before-stale: 60
|
||||||
|
|
|
@ -35,7 +35,7 @@ COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# Stage 3: Build go proxy
|
# Stage 3: Build go proxy
|
||||||
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.4-bookworm AS go-builder
|
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.5-bookworm AS go-builder
|
||||||
|
|
||||||
ARG TARGETOS
|
ARG TARGETOS
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
|
@ -121,7 +121,7 @@ WORKDIR /
|
||||||
# We cannot cache this layer otherwise we'll end up with a bigger image
|
# We cannot cache this layer otherwise we'll end up with a bigger image
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
# Required for runtime
|
# Required for runtime
|
||||||
apt-get install -y --no-install-recommends libpq5 openssl libxmlsec1-openssl libmaxminddb0 && \
|
apt-get install -y --no-install-recommends libpq5 openssl libxmlsec1-openssl libmaxminddb0 ca-certificates && \
|
||||||
# Required for bootstrap & healtcheck
|
# Required for bootstrap & healtcheck
|
||||||
apt-get install -y --no-install-recommends runit && \
|
apt-get install -y --no-install-recommends runit && \
|
||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
|
|
|
@ -21,7 +21,9 @@ _other_urls = []
|
||||||
for _authentik_app in get_apps():
|
for _authentik_app in get_apps():
|
||||||
try:
|
try:
|
||||||
api_urls = import_module(f"{_authentik_app.name}.urls")
|
api_urls = import_module(f"{_authentik_app.name}.urls")
|
||||||
except (ModuleNotFoundError, ImportError) as exc:
|
except ModuleNotFoundError:
|
||||||
|
continue
|
||||||
|
except ImportError as exc:
|
||||||
LOGGER.warning("Could not import app's URLs", app_name=_authentik_app.name, exc=exc)
|
LOGGER.warning("Could not import app's URLs", app_name=_authentik_app.name, exc=exc)
|
||||||
continue
|
continue
|
||||||
if not hasattr(api_urls, "api_urlpatterns"):
|
if not hasattr(api_urls, "api_urlpatterns"):
|
||||||
|
|
|
@ -75,13 +75,13 @@ class BlueprintEventHandler(FileSystemEventHandler):
|
||||||
return
|
return
|
||||||
if event.is_directory:
|
if event.is_directory:
|
||||||
return
|
return
|
||||||
if isinstance(event, FileCreatedEvent):
|
|
||||||
LOGGER.debug("new blueprint file created, starting discovery")
|
|
||||||
blueprints_discovery.delay()
|
|
||||||
if isinstance(event, FileModifiedEvent):
|
|
||||||
path = Path(event.src_path)
|
|
||||||
root = Path(CONFIG.get("blueprints_dir")).absolute()
|
root = Path(CONFIG.get("blueprints_dir")).absolute()
|
||||||
|
path = Path(event.src_path).absolute()
|
||||||
rel_path = str(path.relative_to(root))
|
rel_path = str(path.relative_to(root))
|
||||||
|
if isinstance(event, FileCreatedEvent):
|
||||||
|
LOGGER.debug("new blueprint file created, starting discovery", path=rel_path)
|
||||||
|
blueprints_discovery.delay(rel_path)
|
||||||
|
if isinstance(event, FileModifiedEvent):
|
||||||
for instance in BlueprintInstance.objects.filter(path=rel_path, enabled=True):
|
for instance in BlueprintInstance.objects.filter(path=rel_path, enabled=True):
|
||||||
LOGGER.debug("modified blueprint file, starting apply", instance=instance)
|
LOGGER.debug("modified blueprint file, starting apply", instance=instance)
|
||||||
apply_blueprint.delay(instance.pk.hex)
|
apply_blueprint.delay(instance.pk.hex)
|
||||||
|
@ -98,39 +98,32 @@ def blueprints_find_dict():
|
||||||
return blueprints
|
return blueprints
|
||||||
|
|
||||||
|
|
||||||
def blueprints_find():
|
def blueprints_find() -> list[BlueprintFile]:
|
||||||
"""Find blueprints and return valid ones"""
|
"""Find blueprints and return valid ones"""
|
||||||
blueprints = []
|
blueprints = []
|
||||||
root = Path(CONFIG.get("blueprints_dir"))
|
root = Path(CONFIG.get("blueprints_dir"))
|
||||||
for path in root.rglob("**/*.yaml"):
|
for path in root.rglob("**/*.yaml"):
|
||||||
|
rel_path = path.relative_to(root)
|
||||||
# Check if any part in the path starts with a dot and assume a hidden file
|
# Check if any part in the path starts with a dot and assume a hidden file
|
||||||
if any(part for part in path.parts if part.startswith(".")):
|
if any(part for part in path.parts if part.startswith(".")):
|
||||||
continue
|
continue
|
||||||
LOGGER.debug("found blueprint", path=str(path))
|
|
||||||
with open(path, "r", encoding="utf-8") as blueprint_file:
|
with open(path, "r", encoding="utf-8") as blueprint_file:
|
||||||
try:
|
try:
|
||||||
raw_blueprint = load(blueprint_file.read(), BlueprintLoader)
|
raw_blueprint = load(blueprint_file.read(), BlueprintLoader)
|
||||||
except YAMLError as exc:
|
except YAMLError as exc:
|
||||||
raw_blueprint = None
|
raw_blueprint = None
|
||||||
LOGGER.warning("failed to parse blueprint", exc=exc, path=str(path))
|
LOGGER.warning("failed to parse blueprint", exc=exc, path=str(rel_path))
|
||||||
if not raw_blueprint:
|
if not raw_blueprint:
|
||||||
continue
|
continue
|
||||||
metadata = raw_blueprint.get("metadata", None)
|
metadata = raw_blueprint.get("metadata", None)
|
||||||
version = raw_blueprint.get("version", 1)
|
version = raw_blueprint.get("version", 1)
|
||||||
if version != 1:
|
if version != 1:
|
||||||
LOGGER.warning("invalid blueprint version", version=version, path=str(path))
|
LOGGER.warning("invalid blueprint version", version=version, path=str(rel_path))
|
||||||
continue
|
continue
|
||||||
file_hash = sha512(path.read_bytes()).hexdigest()
|
file_hash = sha512(path.read_bytes()).hexdigest()
|
||||||
blueprint = BlueprintFile(
|
blueprint = BlueprintFile(str(rel_path), version, file_hash, int(path.stat().st_mtime))
|
||||||
str(path.relative_to(root)), version, file_hash, int(path.stat().st_mtime)
|
|
||||||
)
|
|
||||||
blueprint.meta = from_dict(BlueprintMetadata, metadata) if metadata else None
|
blueprint.meta = from_dict(BlueprintMetadata, metadata) if metadata else None
|
||||||
blueprints.append(blueprint)
|
blueprints.append(blueprint)
|
||||||
LOGGER.debug(
|
|
||||||
"parsed & loaded blueprint",
|
|
||||||
hash=file_hash,
|
|
||||||
path=str(path),
|
|
||||||
)
|
|
||||||
return blueprints
|
return blueprints
|
||||||
|
|
||||||
|
|
||||||
|
@ -138,10 +131,12 @@ def blueprints_find():
|
||||||
throws=(DatabaseError, ProgrammingError, InternalError), base=MonitoredTask, bind=True
|
throws=(DatabaseError, ProgrammingError, InternalError), base=MonitoredTask, bind=True
|
||||||
)
|
)
|
||||||
@prefill_task
|
@prefill_task
|
||||||
def blueprints_discovery(self: MonitoredTask):
|
def blueprints_discovery(self: MonitoredTask, path: Optional[str] = None):
|
||||||
"""Find blueprints and check if they need to be created in the database"""
|
"""Find blueprints and check if they need to be created in the database"""
|
||||||
count = 0
|
count = 0
|
||||||
for blueprint in blueprints_find():
|
for blueprint in blueprints_find():
|
||||||
|
if path and blueprint.path != path:
|
||||||
|
continue
|
||||||
check_blueprint_v1_file(blueprint)
|
check_blueprint_v1_file(blueprint)
|
||||||
count += 1
|
count += 1
|
||||||
self.set_status(
|
self.set_status(
|
||||||
|
@ -171,7 +166,11 @@ def check_blueprint_v1_file(blueprint: BlueprintFile):
|
||||||
metadata={},
|
metadata={},
|
||||||
)
|
)
|
||||||
instance.save()
|
instance.save()
|
||||||
|
LOGGER.info(
|
||||||
|
"Creating new blueprint instance from file", instance=instance, path=instance.path
|
||||||
|
)
|
||||||
if instance.last_applied_hash != blueprint.hash:
|
if instance.last_applied_hash != blueprint.hash:
|
||||||
|
LOGGER.info("Applying blueprint due to changed file", instance=instance, path=instance.path)
|
||||||
apply_blueprint.delay(str(instance.pk))
|
apply_blueprint.delay(str(instance.pk))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ class SourceSerializer(ModelSerializer, MetaNameSerializer):
|
||||||
|
|
||||||
managed = ReadOnlyField()
|
managed = ReadOnlyField()
|
||||||
component = SerializerMethodField()
|
component = SerializerMethodField()
|
||||||
icon = ReadOnlyField(source="get_icon")
|
icon = ReadOnlyField(source="icon_url")
|
||||||
|
|
||||||
def get_component(self, obj: Source) -> str:
|
def get_component(self, obj: Source) -> str:
|
||||||
"""Get object component so that we know how to edit the object"""
|
"""Get object component so that we know how to edit the object"""
|
||||||
|
|
|
@ -27,7 +27,7 @@ window.authentik.flow = {
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<ak-message-container></ak-message-container>
|
<ak-message-container></ak-message-container>
|
||||||
<ak-flow-executor>
|
<ak-flow-executor flowSlug="{{ flow.slug }}">
|
||||||
<ak-loading></ak-loading>
|
<ak-loading></ak-loading>
|
||||||
</ak-flow-executor>
|
</ak-flow-executor>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -5,7 +5,7 @@ from json import loads
|
||||||
import django_filters
|
import django_filters
|
||||||
from django.db.models.aggregates import Count
|
from django.db.models.aggregates import Count
|
||||||
from django.db.models.fields.json import KeyTextTransform, KeyTransform
|
from django.db.models.fields.json import KeyTextTransform, KeyTransform
|
||||||
from django.db.models.functions import ExtractDay
|
from django.db.models.functions import ExtractDay, ExtractHour
|
||||||
from drf_spectacular.types import OpenApiTypes
|
from drf_spectacular.types import OpenApiTypes
|
||||||
from drf_spectacular.utils import OpenApiParameter, extend_schema
|
from drf_spectacular.utils import OpenApiParameter, extend_schema
|
||||||
from guardian.shortcuts import get_objects_for_user
|
from guardian.shortcuts import get_objects_for_user
|
||||||
|
@ -149,7 +149,15 @@ class EventViewSet(ModelViewSet):
|
||||||
return Response(EventTopPerUserSerializer(instance=events, many=True).data)
|
return Response(EventTopPerUserSerializer(instance=events, many=True).data)
|
||||||
|
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
methods=["GET"],
|
responses={200: CoordinateSerializer(many=True)},
|
||||||
|
)
|
||||||
|
@action(detail=False, methods=["GET"], pagination_class=None)
|
||||||
|
def volume(self, request: Request) -> Response:
|
||||||
|
"""Get event volume for specified filters and timeframe"""
|
||||||
|
queryset = self.filter_queryset(self.get_queryset())
|
||||||
|
return Response(queryset.get_events_per(timedelta(days=7), ExtractHour, 7 * 3))
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
responses={200: CoordinateSerializer(many=True)},
|
responses={200: CoordinateSerializer(many=True)},
|
||||||
filters=[],
|
filters=[],
|
||||||
parameters=[
|
parameters=[
|
||||||
|
|
|
@ -167,7 +167,11 @@ class ChallengeStageView(StageView):
|
||||||
stage_type=self.__class__.__name__, method="get_challenge"
|
stage_type=self.__class__.__name__, method="get_challenge"
|
||||||
).time(),
|
).time(),
|
||||||
):
|
):
|
||||||
|
try:
|
||||||
challenge = self.get_challenge(*args, **kwargs)
|
challenge = self.get_challenge(*args, **kwargs)
|
||||||
|
except StageInvalidException as exc:
|
||||||
|
self.logger.debug("Got StageInvalidException", exc=exc)
|
||||||
|
return self.executor.stage_invalid()
|
||||||
with Hub.current.start_span(
|
with Hub.current.start_span(
|
||||||
op="authentik.flow.stage._get_challenge",
|
op="authentik.flow.stage._get_challenge",
|
||||||
description=self.__class__.__name__,
|
description=self.__class__.__name__,
|
||||||
|
|
|
@ -13,7 +13,11 @@ from authentik.lib.config import CONFIG
|
||||||
from authentik.lib.logging import get_logger_config, structlog_configure
|
from authentik.lib.logging import get_logger_config, structlog_configure
|
||||||
from authentik.lib.sentry import sentry_init
|
from authentik.lib.sentry import sentry_init
|
||||||
from authentik.lib.utils.reflection import get_env
|
from authentik.lib.utils.reflection import get_env
|
||||||
from authentik.stages.password import BACKEND_APP_PASSWORD, BACKEND_INBUILT, BACKEND_LDAP
|
from authentik.stages.password import (
|
||||||
|
BACKEND_APP_PASSWORD,
|
||||||
|
BACKEND_INBUILT,
|
||||||
|
BACKEND_LDAP,
|
||||||
|
)
|
||||||
|
|
||||||
BASE_DIR = Path(__file__).absolute().parent.parent.parent
|
BASE_DIR = Path(__file__).absolute().parent.parent.parent
|
||||||
STATICFILES_DIRS = [BASE_DIR / Path("web")]
|
STATICFILES_DIRS = [BASE_DIR / Path("web")]
|
||||||
|
@ -129,7 +133,9 @@ SPECTACULAR_SETTINGS = {
|
||||||
"CONTACT": {
|
"CONTACT": {
|
||||||
"email": "hello@goauthentik.io",
|
"email": "hello@goauthentik.io",
|
||||||
},
|
},
|
||||||
"AUTHENTICATION_WHITELIST": ["authentik.api.authentication.TokenAuthentication"],
|
"AUTHENTICATION_WHITELIST": [
|
||||||
|
"authentik.api.authentication.TokenAuthentication"
|
||||||
|
],
|
||||||
"LICENSE": {
|
"LICENSE": {
|
||||||
"name": "MIT",
|
"name": "MIT",
|
||||||
"url": "https://github.com/goauthentik/authentik/blob/main/LICENSE",
|
"url": "https://github.com/goauthentik/authentik/blob/main/LICENSE",
|
||||||
|
@ -164,7 +170,9 @@ REST_FRAMEWORK = {
|
||||||
"DEFAULT_PARSER_CLASSES": [
|
"DEFAULT_PARSER_CLASSES": [
|
||||||
"rest_framework.parsers.JSONParser",
|
"rest_framework.parsers.JSONParser",
|
||||||
],
|
],
|
||||||
"DEFAULT_PERMISSION_CLASSES": ("authentik.rbac.permissions.ObjectPermissions",),
|
"DEFAULT_PERMISSION_CLASSES": (
|
||||||
|
"authentik.rbac.permissions.ObjectPermissions",
|
||||||
|
),
|
||||||
"DEFAULT_AUTHENTICATION_CLASSES": (
|
"DEFAULT_AUTHENTICATION_CLASSES": (
|
||||||
"authentik.api.authentication.TokenAuthentication",
|
"authentik.api.authentication.TokenAuthentication",
|
||||||
"rest_framework.authentication.SessionAuthentication",
|
"rest_framework.authentication.SessionAuthentication",
|
||||||
|
@ -184,7 +192,9 @@ _redis_protocol_prefix = "redis://"
|
||||||
_redis_celery_tls_requirements = ""
|
_redis_celery_tls_requirements = ""
|
||||||
if CONFIG.get_bool("redis.tls", False):
|
if CONFIG.get_bool("redis.tls", False):
|
||||||
_redis_protocol_prefix = "rediss://"
|
_redis_protocol_prefix = "rediss://"
|
||||||
_redis_celery_tls_requirements = f"?ssl_cert_reqs={CONFIG.get('redis.tls_reqs')}"
|
_redis_celery_tls_requirements = (
|
||||||
|
f"?ssl_cert_reqs={CONFIG.get('redis.tls_reqs')}"
|
||||||
|
)
|
||||||
_redis_url = (
|
_redis_url = (
|
||||||
f"{_redis_protocol_prefix}:"
|
f"{_redis_protocol_prefix}:"
|
||||||
f"{quote_plus(CONFIG.get('redis.password'))}@{quote_plus(CONFIG.get('redis.host'))}:"
|
f"{quote_plus(CONFIG.get('redis.password'))}@{quote_plus(CONFIG.get('redis.host'))}:"
|
||||||
|
@ -194,7 +204,8 @@ _redis_url = (
|
||||||
CACHES = {
|
CACHES = {
|
||||||
"default": {
|
"default": {
|
||||||
"BACKEND": "django_redis.cache.RedisCache",
|
"BACKEND": "django_redis.cache.RedisCache",
|
||||||
"LOCATION": CONFIG.get("cache.url") or f"{_redis_url}/{CONFIG.get('redis.db')}",
|
"LOCATION": CONFIG.get("cache.url")
|
||||||
|
or f"{_redis_url}/{CONFIG.get('redis.db')}",
|
||||||
"TIMEOUT": CONFIG.get_int("cache.timeout", 300),
|
"TIMEOUT": CONFIG.get_int("cache.timeout", 300),
|
||||||
"OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"},
|
"OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"},
|
||||||
"KEY_PREFIX": "authentik_cache",
|
"KEY_PREFIX": "authentik_cache",
|
||||||
|
@ -255,7 +266,11 @@ CHANNEL_LAYERS = {
|
||||||
"default": {
|
"default": {
|
||||||
"BACKEND": "channels_redis.pubsub.RedisPubSubChannelLayer",
|
"BACKEND": "channels_redis.pubsub.RedisPubSubChannelLayer",
|
||||||
"CONFIG": {
|
"CONFIG": {
|
||||||
"hosts": [CONFIG.get("channel.url", f"{_redis_url}/{CONFIG.get('redis.db')}")],
|
"hosts": [
|
||||||
|
CONFIG.get(
|
||||||
|
"channel.url", f"{_redis_url}/{CONFIG.get('redis.db')}"
|
||||||
|
)
|
||||||
|
],
|
||||||
"prefix": "authentik_channels_",
|
"prefix": "authentik_channels_",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -313,7 +328,9 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||||
},
|
},
|
||||||
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
|
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
|
||||||
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
|
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
|
||||||
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
|
{
|
||||||
|
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -350,7 +367,9 @@ CELERY = {
|
||||||
"task_default_queue": "authentik",
|
"task_default_queue": "authentik",
|
||||||
"broker_url": CONFIG.get("broker.url")
|
"broker_url": CONFIG.get("broker.url")
|
||||||
or f"{_redis_url}/{CONFIG.get('redis.db')}{_redis_celery_tls_requirements}",
|
or f"{_redis_url}/{CONFIG.get('redis.db')}{_redis_celery_tls_requirements}",
|
||||||
"broker_transport_options": CONFIG.get_dict_from_b64_json("broker.transport_options"),
|
"broker_transport_options": CONFIG.get_dict_from_b64_json(
|
||||||
|
"broker.transport_options"
|
||||||
|
),
|
||||||
"result_backend": CONFIG.get("result_backend.url")
|
"result_backend": CONFIG.get("result_backend.url")
|
||||||
or f"{_redis_url}/{CONFIG.get('redis.db')}{_redis_celery_tls_requirements}",
|
or f"{_redis_url}/{CONFIG.get('redis.db')}{_redis_celery_tls_requirements}",
|
||||||
}
|
}
|
||||||
|
@ -361,7 +380,10 @@ _ERROR_REPORTING = CONFIG.get_bool("error_reporting.enabled", False)
|
||||||
if _ERROR_REPORTING:
|
if _ERROR_REPORTING:
|
||||||
sentry_env = CONFIG.get("error_reporting.environment", "customer")
|
sentry_env = CONFIG.get("error_reporting.environment", "customer")
|
||||||
sentry_init()
|
sentry_init()
|
||||||
set_tag("authentik.uuid", sha512(str(SECRET_KEY).encode("ascii")).hexdigest()[:16])
|
set_tag(
|
||||||
|
"authentik.uuid",
|
||||||
|
sha512(str(SECRET_KEY).encode("ascii")).hexdigest()[:16],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
|
@ -391,8 +413,12 @@ def _update_settings(app_path: str):
|
||||||
CONFIG.log("debug", "Loaded app settings", path=app_path)
|
CONFIG.log("debug", "Loaded app settings", path=app_path)
|
||||||
INSTALLED_APPS.extend(getattr(settings_module, "INSTALLED_APPS", []))
|
INSTALLED_APPS.extend(getattr(settings_module, "INSTALLED_APPS", []))
|
||||||
MIDDLEWARE.extend(getattr(settings_module, "MIDDLEWARE", []))
|
MIDDLEWARE.extend(getattr(settings_module, "MIDDLEWARE", []))
|
||||||
AUTHENTICATION_BACKENDS.extend(getattr(settings_module, "AUTHENTICATION_BACKENDS", []))
|
AUTHENTICATION_BACKENDS.extend(
|
||||||
CELERY["beat_schedule"].update(getattr(settings_module, "CELERY_BEAT_SCHEDULE", {}))
|
getattr(settings_module, "AUTHENTICATION_BACKENDS", [])
|
||||||
|
)
|
||||||
|
CELERY["beat_schedule"].update(
|
||||||
|
getattr(settings_module, "CELERY_BEAT_SCHEDULE", {})
|
||||||
|
)
|
||||||
for _attr in dir(settings_module):
|
for _attr in dir(settings_module):
|
||||||
if not _attr.startswith("__") and _attr not in _DISALLOWED_ITEMS:
|
if not _attr.startswith("__") and _attr not in _DISALLOWED_ITEMS:
|
||||||
globals()[_attr] = getattr(settings_module, _attr)
|
globals()[_attr] = getattr(settings_module, _attr)
|
||||||
|
@ -411,7 +437,6 @@ if DEBUG:
|
||||||
CELERY["task_always_eager"] = True
|
CELERY["task_always_eager"] = True
|
||||||
os.environ[ENV_GIT_HASH_KEY] = "dev"
|
os.environ[ENV_GIT_HASH_KEY] = "dev"
|
||||||
INSTALLED_APPS.append("silk")
|
INSTALLED_APPS.append("silk")
|
||||||
SILKY_PYTHON_PROFILER = False
|
|
||||||
MIDDLEWARE = ["silk.middleware.SilkyMiddleware"] + MIDDLEWARE
|
MIDDLEWARE = ["silk.middleware.SilkyMiddleware"] + MIDDLEWARE
|
||||||
REST_FRAMEWORK["DEFAULT_RENDERER_CLASSES"].append(
|
REST_FRAMEWORK["DEFAULT_RENDERER_CLASSES"].append(
|
||||||
"rest_framework.renderers.BrowsableAPIRenderer"
|
"rest_framework.renderers.BrowsableAPIRenderer"
|
||||||
|
|
|
@ -5,6 +5,7 @@ from uuid import uuid4
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.http.request import QueryDict
|
from django.http.request import QueryDict
|
||||||
|
from django.template.exceptions import TemplateSyntaxError
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
@ -12,11 +13,14 @@ from django.utils.translation import gettext as _
|
||||||
from rest_framework.fields import CharField
|
from rest_framework.fields import CharField
|
||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
|
|
||||||
|
from authentik.events.models import Event, EventAction
|
||||||
from authentik.flows.challenge import Challenge, ChallengeResponse, ChallengeTypes
|
from authentik.flows.challenge import Challenge, ChallengeResponse, ChallengeTypes
|
||||||
|
from authentik.flows.exceptions import StageInvalidException
|
||||||
from authentik.flows.models import FlowDesignation, FlowToken
|
from authentik.flows.models import FlowDesignation, FlowToken
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_IS_RESTORED, PLAN_CONTEXT_PENDING_USER
|
from authentik.flows.planner import PLAN_CONTEXT_IS_RESTORED, PLAN_CONTEXT_PENDING_USER
|
||||||
from authentik.flows.stage import ChallengeStageView
|
from authentik.flows.stage import ChallengeStageView
|
||||||
from authentik.flows.views.executor import QS_KEY_TOKEN, QS_QUERY
|
from authentik.flows.views.executor import QS_KEY_TOKEN, QS_QUERY
|
||||||
|
from authentik.lib.utils.errors import exception_to_string
|
||||||
from authentik.stages.email.models import EmailStage
|
from authentik.stages.email.models import EmailStage
|
||||||
from authentik.stages.email.tasks import send_mails
|
from authentik.stages.email.tasks import send_mails
|
||||||
from authentik.stages.email.utils import TemplateEmailMessage
|
from authentik.stages.email.utils import TemplateEmailMessage
|
||||||
|
@ -103,6 +107,7 @@ class EmailStageView(ChallengeStageView):
|
||||||
current_stage: EmailStage = self.executor.current_stage
|
current_stage: EmailStage = self.executor.current_stage
|
||||||
token = self.get_token()
|
token = self.get_token()
|
||||||
# Send mail to user
|
# Send mail to user
|
||||||
|
try:
|
||||||
message = TemplateEmailMessage(
|
message = TemplateEmailMessage(
|
||||||
subject=_(current_stage.subject),
|
subject=_(current_stage.subject),
|
||||||
to=[email],
|
to=[email],
|
||||||
|
@ -115,6 +120,14 @@ class EmailStageView(ChallengeStageView):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
send_mails(current_stage, message)
|
send_mails(current_stage, message)
|
||||||
|
except TemplateSyntaxError as exc:
|
||||||
|
Event.new(
|
||||||
|
EventAction.CONFIGURATION_ERROR,
|
||||||
|
message=_("Exception occurred while rendering E-mail template"),
|
||||||
|
error=exception_to_string(exc),
|
||||||
|
template=current_stage.template,
|
||||||
|
).from_http(self.request)
|
||||||
|
raise StageInvalidException from exc
|
||||||
|
|
||||||
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||||
# Check if the user came back from the email link to verify
|
# Check if the user came back from the email link to verify
|
||||||
|
@ -135,7 +148,11 @@ class EmailStageView(ChallengeStageView):
|
||||||
return self.executor.stage_invalid()
|
return self.executor.stage_invalid()
|
||||||
# Check if we've already sent the initial e-mail
|
# Check if we've already sent the initial e-mail
|
||||||
if PLAN_CONTEXT_EMAIL_SENT not in self.executor.plan.context:
|
if PLAN_CONTEXT_EMAIL_SENT not in self.executor.plan.context:
|
||||||
|
try:
|
||||||
self.send_email()
|
self.send_email()
|
||||||
|
except StageInvalidException as exc:
|
||||||
|
self.logger.debug("Got StageInvalidException", exc=exc)
|
||||||
|
return self.executor.stage_invalid()
|
||||||
self.executor.plan.context[PLAN_CONTEXT_EMAIL_SENT] = True
|
self.executor.plan.context[PLAN_CONTEXT_EMAIL_SENT] = True
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,20 @@ from pathlib import Path
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
from tempfile import mkdtemp, mkstemp
|
from tempfile import mkdtemp, mkstemp
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from unittest.mock import PropertyMock, patch
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.test import TestCase
|
from django.core.mail.backends.locmem import EmailBackend
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
from authentik.stages.email.models import get_template_choices
|
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||||
|
from authentik.events.models import Event, EventAction
|
||||||
|
from authentik.flows.markers import StageMarker
|
||||||
|
from authentik.flows.models import FlowDesignation, FlowStageBinding
|
||||||
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
||||||
|
from authentik.flows.tests import FlowTestCase
|
||||||
|
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||||
|
from authentik.stages.email.models import EmailStage, get_template_choices
|
||||||
|
|
||||||
|
|
||||||
def get_templates_setting(temp_dir: str) -> dict[str, Any]:
|
def get_templates_setting(temp_dir: str) -> dict[str, Any]:
|
||||||
|
@ -18,11 +27,18 @@ def get_templates_setting(temp_dir: str) -> dict[str, Any]:
|
||||||
return templates_setting
|
return templates_setting
|
||||||
|
|
||||||
|
|
||||||
class TestEmailStageTemplates(TestCase):
|
class TestEmailStageTemplates(FlowTestCase):
|
||||||
"""Email tests"""
|
"""Email tests"""
|
||||||
|
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
self.dir = mkdtemp()
|
self.dir = Path(mkdtemp())
|
||||||
|
self.user = create_test_admin_user()
|
||||||
|
|
||||||
|
self.flow = create_test_flow(FlowDesignation.AUTHENTICATION)
|
||||||
|
self.stage = EmailStage.objects.create(
|
||||||
|
name="email",
|
||||||
|
)
|
||||||
|
self.binding = FlowStageBinding.objects.create(target=self.flow, stage=self.stage, order=2)
|
||||||
|
|
||||||
def tearDown(self) -> None:
|
def tearDown(self) -> None:
|
||||||
rmtree(self.dir)
|
rmtree(self.dir)
|
||||||
|
@ -38,3 +54,37 @@ class TestEmailStageTemplates(TestCase):
|
||||||
self.assertEqual(len(choices), 3)
|
self.assertEqual(len(choices), 3)
|
||||||
unlink(file)
|
unlink(file)
|
||||||
unlink(file2)
|
unlink(file2)
|
||||||
|
|
||||||
|
def test_custom_template_invalid_syntax(self):
|
||||||
|
"""Test with custom template"""
|
||||||
|
with open(self.dir / Path("invalid.html"), "w+", encoding="utf-8") as _invalid:
|
||||||
|
_invalid.write("{% blocktranslate %}")
|
||||||
|
with self.settings(TEMPLATES=get_templates_setting(self.dir)):
|
||||||
|
self.stage.template = "invalid.html"
|
||||||
|
plan = FlowPlan(
|
||||||
|
flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]
|
||||||
|
)
|
||||||
|
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
|
||||||
|
session = self.client.session
|
||||||
|
session[SESSION_KEY_PLAN] = plan
|
||||||
|
session.save()
|
||||||
|
|
||||||
|
url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||||
|
with patch(
|
||||||
|
"authentik.stages.email.models.EmailStage.backend_class",
|
||||||
|
PropertyMock(return_value=EmailBackend),
|
||||||
|
):
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertStageResponse(
|
||||||
|
response,
|
||||||
|
self.flow,
|
||||||
|
error_message="Unknown error",
|
||||||
|
)
|
||||||
|
events = Event.objects.filter(action=EventAction.CONFIGURATION_ERROR)
|
||||||
|
self.assertEqual(len(events), 1)
|
||||||
|
event = events.first()
|
||||||
|
self.assertEqual(
|
||||||
|
event.context["message"], "Exception occurred while rendering E-mail template"
|
||||||
|
)
|
||||||
|
self.assertEqual(event.context["template"], "invalid.html")
|
||||||
|
|
16
go.mod
16
go.mod
|
@ -10,7 +10,7 @@ require (
|
||||||
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
|
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
|
||||||
github.com/go-ldap/ldap/v3 v3.4.6
|
github.com/go-ldap/ldap/v3 v3.4.6
|
||||||
github.com/go-openapi/runtime v0.26.0
|
github.com/go-openapi/runtime v0.26.0
|
||||||
github.com/go-openapi/strfmt v0.21.7
|
github.com/go-openapi/strfmt v0.21.9
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||||
github.com/google/uuid v1.4.0
|
github.com/google/uuid v1.4.0
|
||||||
github.com/gorilla/handlers v1.5.2
|
github.com/gorilla/handlers v1.5.2
|
||||||
|
@ -27,9 +27,9 @@ require (
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spf13/cobra v1.8.0
|
github.com/spf13/cobra v1.8.0
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
goauthentik.io/api/v3 v3.2023104.1
|
goauthentik.io/api/v3 v3.2023104.2
|
||||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||||
golang.org/x/oauth2 v0.14.0
|
golang.org/x/oauth2 v0.15.0
|
||||||
golang.org/x/sync v0.5.0
|
golang.org/x/sync v0.5.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
layeh.com/radius v0.0.0-20210819152912-ad72663a72ab
|
layeh.com/radius v0.0.0-20210819152912-ad72663a72ab
|
||||||
|
@ -49,7 +49,7 @@ require (
|
||||||
github.com/go-logr/logr v1.2.3 // indirect
|
github.com/go-logr/logr v1.2.3 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-openapi/analysis v0.21.4 // indirect
|
github.com/go-openapi/analysis v0.21.4 // indirect
|
||||||
github.com/go-openapi/errors v0.20.3 // indirect
|
github.com/go-openapi/errors v0.20.4 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||||
github.com/go-openapi/loads v0.21.2 // indirect
|
github.com/go-openapi/loads v0.21.2 // indirect
|
||||||
|
@ -69,12 +69,12 @@ require (
|
||||||
github.com/prometheus/common v0.44.0 // indirect
|
github.com/prometheus/common v0.44.0 // indirect
|
||||||
github.com/prometheus/procfs v0.11.1 // indirect
|
github.com/prometheus/procfs v0.11.1 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
go.mongodb.org/mongo-driver v1.11.3 // indirect
|
go.mongodb.org/mongo-driver v1.13.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.14.0 // indirect
|
go.opentelemetry.io/otel v1.14.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.14.0 // indirect
|
go.opentelemetry.io/otel/trace v1.14.0 // indirect
|
||||||
golang.org/x/crypto v0.15.0 // indirect
|
golang.org/x/crypto v0.16.0 // indirect
|
||||||
golang.org/x/net v0.18.0 // indirect
|
golang.org/x/net v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.14.0 // indirect
|
golang.org/x/sys v0.15.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/protobuf v1.31.0 // indirect
|
google.golang.org/protobuf v1.31.0 // indirect
|
||||||
|
|
36
go.sum
36
go.sum
|
@ -103,8 +103,8 @@ github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9Qy
|
||||||
github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||||
github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||||
github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||||
github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc=
|
github.com/go-openapi/errors v0.20.4 h1:unTcVm6PispJsMECE3zWgvG4xTiKda1LIR5rCRWLG6M=
|
||||||
github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk=
|
github.com/go-openapi/errors v0.20.4/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk=
|
||||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
|
@ -123,8 +123,8 @@ github.com/go-openapi/spec v0.20.8/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6
|
||||||
github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg=
|
github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg=
|
||||||
github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k=
|
github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k=
|
||||||
github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg=
|
github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg=
|
||||||
github.com/go-openapi/strfmt v0.21.7 h1:rspiXgNWgeUzhjo1YU01do6qsahtJNByjLVbPLNHb8k=
|
github.com/go-openapi/strfmt v0.21.9 h1:LnEGOO9qyEC1v22Bzr323M98G13paIUGPU7yeJtG9Xs=
|
||||||
github.com/go-openapi/strfmt v0.21.7/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew=
|
github.com/go-openapi/strfmt v0.21.9/go.mod h1:0k3v301mglEaZRJdDDGSlN6Npq4VMVU69DE0LUyf7uA=
|
||||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||||
github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||||
|
@ -328,13 +328,14 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/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/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
||||||
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
|
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
|
||||||
|
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||||
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
|
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
|
||||||
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
|
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
|
||||||
|
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
@ -343,8 +344,8 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
|
||||||
go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg=
|
go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg=
|
||||||
go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng=
|
go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng=
|
||||||
go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8=
|
go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8=
|
||||||
go.mongodb.org/mongo-driver v1.11.3 h1:Ql6K6qYHEzB6xvu4+AU0BoRoqf9vFPcc4o7MUIdPW8Y=
|
go.mongodb.org/mongo-driver v1.13.0 h1:67DgFFjYOCMWdtTEmKFpV3ffWlFnh+CYZ8ZS/tXWUfY=
|
||||||
go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
|
go.mongodb.org/mongo-driver v1.13.0/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
@ -358,8 +359,8 @@ go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyK
|
||||||
go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
|
go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
|
||||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||||
goauthentik.io/api/v3 v3.2023104.1 h1:cvAsgoKP/fmO4fzifx0OyICknauFeyN88C4Z1LdWXDs=
|
goauthentik.io/api/v3 v3.2023104.2 h1:TV3SdaPGhjVE7If0l1kt+H23xwgEabWUFgX4ijkkeSc=
|
||||||
goauthentik.io/api/v3 v3.2023104.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
goauthentik.io/api/v3 v3.2023104.2/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
|
@ -372,8 +373,8 @@ golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPh
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||||
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
|
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
||||||
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
|
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
@ -440,16 +441,16 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||||
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0=
|
golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
|
||||||
golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM=
|
golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -504,8 +505,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
@ -519,6 +520,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
|
|
@ -147,7 +147,11 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
|
||||||
fg := api.NewGroup(g.Pk, g.NumPk, g.Name, g.ParentName, []api.GroupMember{u}, []api.Role{})
|
fg := api.NewGroup(g.Pk, g.NumPk, g.Name, g.ParentName, []api.GroupMember{u}, []api.Role{})
|
||||||
fg.SetUsers([]int32{flag.UserPk})
|
fg.SetUsers([]int32{flag.UserPk})
|
||||||
if g.Parent.IsSet() {
|
if g.Parent.IsSet() {
|
||||||
fg.SetParent(*g.Parent.Get())
|
if p := g.Parent.Get(); p != nil {
|
||||||
|
fg.SetParent(*p)
|
||||||
|
} else {
|
||||||
|
fg.SetParentNil()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fg.SetAttributes(g.Attributes)
|
fg.SetAttributes(g.Attributes)
|
||||||
fg.SetIsSuperuser(*g.IsSuperuser)
|
fg.SetIsSuperuser(*g.IsSuperuser)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Stage 1: Build
|
# Stage 1: Build
|
||||||
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.4-bookworm AS builder
|
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.5-bookworm AS builder
|
||||||
|
|
||||||
ARG TARGETOS
|
ARG TARGETOS
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -15,7 +15,7 @@ COPY web .
|
||||||
RUN npm run build-proxy
|
RUN npm run build-proxy
|
||||||
|
|
||||||
# Stage 2: Build
|
# Stage 2: Build
|
||||||
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.4-bookworm AS builder
|
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.5-bookworm AS builder
|
||||||
|
|
||||||
ARG TARGETOS
|
ARG TARGETOS
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Stage 1: Build
|
# Stage 1: Build
|
||||||
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.4-bookworm AS builder
|
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.5-bookworm AS builder
|
||||||
|
|
||||||
ARG TARGETOS
|
ARG TARGETOS
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
|
|
80
schema.yml
80
schema.yml
|
@ -6276,6 +6276,86 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/GenericError'
|
$ref: '#/components/schemas/GenericError'
|
||||||
description: ''
|
description: ''
|
||||||
|
/events/events/volume/:
|
||||||
|
get:
|
||||||
|
operationId: events_events_volume_list
|
||||||
|
description: Get event volume for specified filters and timeframe
|
||||||
|
parameters:
|
||||||
|
- in: query
|
||||||
|
name: action
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: client_ip
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: context_authorized_app
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Context Authorized application
|
||||||
|
- in: query
|
||||||
|
name: context_model_app
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Context Model App
|
||||||
|
- in: query
|
||||||
|
name: context_model_name
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Context Model Name
|
||||||
|
- in: query
|
||||||
|
name: context_model_pk
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Context Model Primary Key
|
||||||
|
- name: ordering
|
||||||
|
required: false
|
||||||
|
in: query
|
||||||
|
description: Which field to use when ordering the results.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- name: search
|
||||||
|
required: false
|
||||||
|
in: query
|
||||||
|
description: A search term.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: tenant_name
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Tenant name
|
||||||
|
- in: query
|
||||||
|
name: username
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Username
|
||||||
|
tags:
|
||||||
|
- events
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Coordinate'
|
||||||
|
description: ''
|
||||||
|
'400':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ValidationError'
|
||||||
|
description: ''
|
||||||
|
'403':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
description: ''
|
||||||
/events/notifications/:
|
/events/notifications/:
|
||||||
get:
|
get:
|
||||||
operationId: events_notifications_list
|
operationId: events_notifications_list
|
||||||
|
|
|
@ -7,19 +7,19 @@
|
||||||
"name": "@goauthentik/web-tests",
|
"name": "@goauthentik/web-tests",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
"@typescript-eslint/eslint-plugin": "^6.13.2",
|
||||||
"@typescript-eslint/parser": "^6.12.0",
|
"@typescript-eslint/parser": "^6.13.2",
|
||||||
"@wdio/cli": "^8.24.1",
|
"@wdio/cli": "^8.24.16",
|
||||||
"@wdio/local-runner": "^8.24.1",
|
"@wdio/local-runner": "^8.24.12",
|
||||||
"@wdio/mocha-framework": "^8.24.0",
|
"@wdio/mocha-framework": "^8.24.12",
|
||||||
"@wdio/spec-reporter": "^8.24.0",
|
"@wdio/spec-reporter": "^8.24.12",
|
||||||
"eslint": "^8.54.0",
|
"eslint": "^8.55.0",
|
||||||
"eslint-config-google": "^0.14.0",
|
"eslint-config-google": "^0.14.0",
|
||||||
"eslint-plugin-sonarjs": "^0.23.0",
|
"eslint-plugin-sonarjs": "^0.23.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^3.1.0",
|
"prettier": "^3.1.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^5.3.2",
|
"typescript": "^5.3.3",
|
||||||
"wdio-wait-for": "^3.0.9"
|
"wdio-wait-for": "^3.0.9"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -332,9 +332,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/eslintrc": {
|
"node_modules/@eslint/eslintrc": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
|
||||||
"integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==",
|
"integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "^6.12.4",
|
"ajv": "^6.12.4",
|
||||||
|
@ -382,9 +382,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "8.54.0",
|
"version": "8.55.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz",
|
||||||
"integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==",
|
"integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
|
@ -946,16 +946,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "6.12.0",
|
"version": "6.13.2",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz",
|
||||||
"integrity": "sha512-XOpZ3IyJUIV1b15M7HVOpgQxPPF7lGXgsfcEIu3yDxFPaf/xZKt7s9QO/pbk7vpWQyVulpJbu4E5LwpZiQo4kA==",
|
"integrity": "sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.5.1",
|
"@eslint-community/regexpp": "^4.5.1",
|
||||||
"@typescript-eslint/scope-manager": "6.12.0",
|
"@typescript-eslint/scope-manager": "6.13.2",
|
||||||
"@typescript-eslint/type-utils": "6.12.0",
|
"@typescript-eslint/type-utils": "6.13.2",
|
||||||
"@typescript-eslint/utils": "6.12.0",
|
"@typescript-eslint/utils": "6.13.2",
|
||||||
"@typescript-eslint/visitor-keys": "6.12.0",
|
"@typescript-eslint/visitor-keys": "6.13.2",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"graphemer": "^1.4.0",
|
"graphemer": "^1.4.0",
|
||||||
"ignore": "^5.2.4",
|
"ignore": "^5.2.4",
|
||||||
|
@ -981,15 +981,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "6.12.0",
|
"version": "6.13.2",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.2.tgz",
|
||||||
"integrity": "sha512-s8/jNFPKPNRmXEnNXfuo1gemBdVmpQsK1pcu+QIvuNJuhFzGrpD7WjOcvDc/+uEdfzSYpNu7U/+MmbScjoQ6vg==",
|
"integrity": "sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "6.12.0",
|
"@typescript-eslint/scope-manager": "6.13.2",
|
||||||
"@typescript-eslint/types": "6.12.0",
|
"@typescript-eslint/types": "6.13.2",
|
||||||
"@typescript-eslint/typescript-estree": "6.12.0",
|
"@typescript-eslint/typescript-estree": "6.13.2",
|
||||||
"@typescript-eslint/visitor-keys": "6.12.0",
|
"@typescript-eslint/visitor-keys": "6.13.2",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -1009,13 +1009,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "6.12.0",
|
"version": "6.13.2",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.2.tgz",
|
||||||
"integrity": "sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==",
|
"integrity": "sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "6.12.0",
|
"@typescript-eslint/types": "6.13.2",
|
||||||
"@typescript-eslint/visitor-keys": "6.12.0"
|
"@typescript-eslint/visitor-keys": "6.13.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.0.0 || >=18.0.0"
|
"node": "^16.0.0 || >=18.0.0"
|
||||||
|
@ -1026,13 +1026,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/type-utils": {
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
"version": "6.12.0",
|
"version": "6.13.2",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.2.tgz",
|
||||||
"integrity": "sha512-WWmRXxhm1X8Wlquj+MhsAG4dU/Blvf1xDgGaYCzfvStP2NwPQh6KBvCDbiOEvaE0filhranjIlK/2fSTVwtBng==",
|
"integrity": "sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/typescript-estree": "6.12.0",
|
"@typescript-eslint/typescript-estree": "6.13.2",
|
||||||
"@typescript-eslint/utils": "6.12.0",
|
"@typescript-eslint/utils": "6.13.2",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"ts-api-utils": "^1.0.1"
|
"ts-api-utils": "^1.0.1"
|
||||||
},
|
},
|
||||||
|
@ -1053,9 +1053,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "6.12.0",
|
"version": "6.13.2",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.2.tgz",
|
||||||
"integrity": "sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==",
|
"integrity": "sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.0.0 || >=18.0.0"
|
"node": "^16.0.0 || >=18.0.0"
|
||||||
|
@ -1066,13 +1066,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "6.12.0",
|
"version": "6.13.2",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.2.tgz",
|
||||||
"integrity": "sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==",
|
"integrity": "sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "6.12.0",
|
"@typescript-eslint/types": "6.13.2",
|
||||||
"@typescript-eslint/visitor-keys": "6.12.0",
|
"@typescript-eslint/visitor-keys": "6.13.2",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"globby": "^11.1.0",
|
"globby": "^11.1.0",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
|
@ -1093,17 +1093,17 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "6.12.0",
|
"version": "6.13.2",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.2.tgz",
|
||||||
"integrity": "sha512-LywPm8h3tGEbgfyjYnu3dauZ0U7R60m+miXgKcZS8c7QALO9uWJdvNoP+duKTk2XMWc7/Q3d/QiCuLN9X6SWyQ==",
|
"integrity": "sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.4.0",
|
"@eslint-community/eslint-utils": "^4.4.0",
|
||||||
"@types/json-schema": "^7.0.12",
|
"@types/json-schema": "^7.0.12",
|
||||||
"@types/semver": "^7.5.0",
|
"@types/semver": "^7.5.0",
|
||||||
"@typescript-eslint/scope-manager": "6.12.0",
|
"@typescript-eslint/scope-manager": "6.13.2",
|
||||||
"@typescript-eslint/types": "6.12.0",
|
"@typescript-eslint/types": "6.13.2",
|
||||||
"@typescript-eslint/typescript-estree": "6.12.0",
|
"@typescript-eslint/typescript-estree": "6.13.2",
|
||||||
"semver": "^7.5.4"
|
"semver": "^7.5.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -1118,12 +1118,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "6.12.0",
|
"version": "6.13.2",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.2.tgz",
|
||||||
"integrity": "sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==",
|
"integrity": "sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "6.12.0",
|
"@typescript-eslint/types": "6.13.2",
|
||||||
"eslint-visitor-keys": "^3.4.1"
|
"eslint-visitor-keys": "^3.4.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -1141,34 +1141,33 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/cli": {
|
"node_modules/@wdio/cli": {
|
||||||
"version": "8.24.1",
|
"version": "8.24.16",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.24.1.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.24.16.tgz",
|
||||||
"integrity": "sha512-3NB5LwPN5f1C8LPumlOHFfoCFfE7OmM0h7vN0/gzjcIlCXMrJ9igePyDQ6Af7u/jqfPk3SloBsG9DnxZBCcAxQ==",
|
"integrity": "sha512-DaXSdkWMlI0pPiTWMJRP5kBGpBrzEJfPdF1VqIw+HBC9vn4OWyZWAOlA3TZ1uKifTJQ3ydaXpclbDW0/x31YhQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.1",
|
"@types/node": "^20.1.1",
|
||||||
"@wdio/config": "8.24.0",
|
"@wdio/config": "8.24.12",
|
||||||
"@wdio/globals": "8.24.1",
|
"@wdio/globals": "8.24.12",
|
||||||
"@wdio/logger": "8.16.17",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/protocols": "8.23.0",
|
"@wdio/protocols": "8.24.12",
|
||||||
"@wdio/types": "8.24.0",
|
"@wdio/types": "8.24.12",
|
||||||
"@wdio/utils": "8.24.0",
|
"@wdio/utils": "8.24.12",
|
||||||
"async-exit-hook": "^2.0.1",
|
"async-exit-hook": "^2.0.1",
|
||||||
"chalk": "^5.2.0",
|
"chalk": "^5.2.0",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"cli-spinners": "^2.9.0",
|
"cli-spinners": "^2.9.0",
|
||||||
"detect-package-manager": "^3.0.1",
|
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"ejs": "^3.1.9",
|
"ejs": "^3.1.9",
|
||||||
"execa": "^8.0.1",
|
"execa": "^8.0.1",
|
||||||
"import-meta-resolve": "^3.0.0",
|
"import-meta-resolve": "^4.0.0",
|
||||||
"inquirer": "9.2.12",
|
"inquirer": "9.2.12",
|
||||||
"lodash.flattendeep": "^4.4.0",
|
"lodash.flattendeep": "^4.4.0",
|
||||||
"lodash.pickby": "^4.6.0",
|
"lodash.pickby": "^4.6.0",
|
||||||
"lodash.union": "^4.6.0",
|
"lodash.union": "^4.6.0",
|
||||||
"read-pkg-up": "^10.0.0",
|
"read-pkg-up": "^10.0.0",
|
||||||
"recursive-readdir": "^2.2.3",
|
"recursive-readdir": "^2.2.3",
|
||||||
"webdriverio": "8.24.1",
|
"webdriverio": "8.24.12",
|
||||||
"yargs": "^17.7.2"
|
"yargs": "^17.7.2"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -1191,47 +1190,47 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/config": {
|
"node_modules/@wdio/config": {
|
||||||
"version": "8.24.0",
|
"version": "8.24.12",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.24.0.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.24.12.tgz",
|
||||||
"integrity": "sha512-n92MPtRCLH763ssS6f/r7uWhnFkIg072nqZK+YnXTlTVIED9SdlMXlyjp9e/1sRmXUc7LbVPwvEVa35lsO0S8w==",
|
"integrity": "sha512-3HW7qG1rIHzOIybV6oHR1CqLghsN0G3Xzs90ZciGL8dYhtcLtYCHwuWmBw4mkaB5xViU4AmZDuj7ChiG8Cr6Qw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wdio/logger": "8.16.17",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/types": "8.24.0",
|
"@wdio/types": "8.24.12",
|
||||||
"@wdio/utils": "8.24.0",
|
"@wdio/utils": "8.24.12",
|
||||||
"decamelize": "^6.0.0",
|
"decamelize": "^6.0.0",
|
||||||
"deepmerge-ts": "^5.0.0",
|
"deepmerge-ts": "^5.0.0",
|
||||||
"glob": "^10.2.2",
|
"glob": "^10.2.2",
|
||||||
"import-meta-resolve": "^3.0.0"
|
"import-meta-resolve": "^4.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.13 || >=18"
|
"node": "^16.13 || >=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/globals": {
|
"node_modules/@wdio/globals": {
|
||||||
"version": "8.24.1",
|
"version": "8.24.12",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.24.1.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.24.12.tgz",
|
||||||
"integrity": "sha512-r5JmeAZd9BiVwUesj8vTRPHybyEMAN/gkscVaawCXEWAm/+7pzLARv6e8PMrAayO4MkeviGBDYCm6d4+nYFOUQ==",
|
"integrity": "sha512-uF26a89Q+6DdqzSfK9suXJNdWYJnsazjzPuq4Xtz6nKdjgmBufSeX1JHV4LxErEu5b/IdzVcMCUKKEvsZPc9vA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.13 || >=18"
|
"node": "^16.13 || >=18"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"expect-webdriverio": "^4.6.1",
|
"expect-webdriverio": "^4.6.1",
|
||||||
"webdriverio": "8.24.1"
|
"webdriverio": "8.24.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/local-runner": {
|
"node_modules/@wdio/local-runner": {
|
||||||
"version": "8.24.1",
|
"version": "8.24.12",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.24.1.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.24.12.tgz",
|
||||||
"integrity": "sha512-/KdUVZn7aY5l1SBWd+xiTKhuO/eSAoFgNDLOArbL98ED2TYDzCZ3QCTlebbMckHA4J4ZZW/Rvox75a9Ne6yRzw==",
|
"integrity": "sha512-Q1lfdSPDEgKwuE1gNucJrkVfgOJLTjtnYGb7Fe7oYUHGDwjkudjSBJYmyx30qFZKfZ4zRqXtaEdys54/0TxibA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.0",
|
"@types/node": "^20.1.0",
|
||||||
"@wdio/logger": "8.16.17",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/repl": "8.23.1",
|
"@wdio/repl": "8.24.12",
|
||||||
"@wdio/runner": "8.24.1",
|
"@wdio/runner": "8.24.12",
|
||||||
"@wdio/types": "8.24.0",
|
"@wdio/types": "8.24.12",
|
||||||
"async-exit-hook": "^2.0.1",
|
"async-exit-hook": "^2.0.1",
|
||||||
"split2": "^4.1.0",
|
"split2": "^4.1.0",
|
||||||
"stream-buffers": "^3.0.2"
|
"stream-buffers": "^3.0.2"
|
||||||
|
@ -1241,9 +1240,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/logger": {
|
"node_modules/@wdio/logger": {
|
||||||
"version": "8.16.17",
|
"version": "8.24.12",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.16.17.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.24.12.tgz",
|
||||||
"integrity": "sha512-zeQ41z3T+b4IsrriZZipayXxLNDuGsm7TdExaviNGojPVrIsQUCSd/FvlLHM32b7ZrMyInHenu/zx1cjAZO71g==",
|
"integrity": "sha512-QisOiVIWKTUCf1H7S+DOtC+gruhlpimQrUXfWMTeeh672PvAJYnTpOJDWA+BtXfsikkUYFAzAaq8SeMJk8rqKg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^5.1.2",
|
"chalk": "^5.1.2",
|
||||||
|
@ -1268,16 +1267,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/mocha-framework": {
|
"node_modules/@wdio/mocha-framework": {
|
||||||
"version": "8.24.0",
|
"version": "8.24.12",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.24.0.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.24.12.tgz",
|
||||||
"integrity": "sha512-UqmvE5Z+KsD+u4mGuV5Y4GUDwrfmX1qIzD07idaLwidU/rHRy+Csn5mzyN38VIrIAuyMYdMGRyxEEieeu6a/4w==",
|
"integrity": "sha512-SHN7CYZnDkVUNYxLp8iMV92xcmU/4gq5dqA0pRrK4m5nIU7BoL0flm0kA+ydYUQyNedQh2ru1V63uNyTOyCKAg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/mocha": "^10.0.0",
|
"@types/mocha": "^10.0.0",
|
||||||
"@types/node": "^20.1.0",
|
"@types/node": "^20.1.0",
|
||||||
"@wdio/logger": "8.16.17",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/types": "8.24.0",
|
"@wdio/types": "8.24.12",
|
||||||
"@wdio/utils": "8.24.0",
|
"@wdio/utils": "8.24.12",
|
||||||
"mocha": "^10.0.0"
|
"mocha": "^10.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -1285,15 +1284,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/protocols": {
|
"node_modules/@wdio/protocols": {
|
||||||
"version": "8.23.0",
|
"version": "8.24.12",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.24.12.tgz",
|
||||||
"integrity": "sha512-2XTzD+lqQP3g8BWn+Bn5BTFzjHqzZNwq7DjlYrb27Bq8nOA+1DEcj3WzQ6V6CktTnKI/LAYKA1IFAF//Azrp/Q==",
|
"integrity": "sha512-QnVj3FkapmVD3h2zoZk+ZQ8gevSj9D9MiIQIy8eOnY4FAneYZ9R9GvoW+mgNcCZO8S8++S/jZHetR8n+8Q808g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/repl": {
|
"node_modules/@wdio/repl": {
|
||||||
"version": "8.23.1",
|
"version": "8.24.12",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-8.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-8.24.12.tgz",
|
||||||
"integrity": "sha512-u6zG2cgBm67V5/WlQzadWqLGXs3moH8MOsgoljULQncelSBBZGZ5DyLB4p7jKcUAsKtMjgmFQmIvpQoqmyvdfg==",
|
"integrity": "sha512-321F3sWafnlw93uRTSjEBVuvWCxTkWNDs7ektQS15drrroL3TMeFOynu4rDrIz0jXD9Vas0HCD2Tq/P0uxFLdw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.0"
|
"@types/node": "^20.1.0"
|
||||||
|
@ -1303,14 +1302,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/reporter": {
|
"node_modules/@wdio/reporter": {
|
||||||
"version": "8.24.0",
|
"version": "8.24.12",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.24.0.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.24.12.tgz",
|
||||||
"integrity": "sha512-yQhUwV5W1oDnVzr1pPBaOOCDwAE0Iyri9sAzIHb4pF1ezdUNTVe8ZfoWZSD4i7oC3riK8MlH8hXfGNCfrFrWlg==",
|
"integrity": "sha512-FtLzDTBXdgxXf4T9HJQ2bNpYYSKEw//jojFm9XzB4fPwzPeFY3HC+dbePucVW1SSLrVzVxqIOyHiwCLqQ/4cQw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.0",
|
"@types/node": "^20.1.0",
|
||||||
"@wdio/logger": "8.16.17",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/types": "8.24.0",
|
"@wdio/types": "8.24.12",
|
||||||
"diff": "^5.0.0",
|
"diff": "^5.0.0",
|
||||||
"object-inspect": "^1.12.0"
|
"object-inspect": "^1.12.0"
|
||||||
},
|
},
|
||||||
|
@ -1319,35 +1318,35 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/runner": {
|
"node_modules/@wdio/runner": {
|
||||||
"version": "8.24.1",
|
"version": "8.24.12",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.24.1.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.24.12.tgz",
|
||||||
"integrity": "sha512-IfbLFUM+/cZoEJCLPjesekQ9FjuH/+OnJPzDIcxEv4RLShZX8mQKmiUw/HNMlQlcOkeAzfTObzaT+f8Tt2ZYlQ==",
|
"integrity": "sha512-wiwXZWG12YDe7GCYBnZ1xEg3UKi18Rvh4RNQiumjypDOErJit1hOCppbJ37LqLqQu+tfWGfN73j46yR7fQOCHw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.0",
|
"@types/node": "^20.1.0",
|
||||||
"@wdio/config": "8.24.0",
|
"@wdio/config": "8.24.12",
|
||||||
"@wdio/globals": "8.24.1",
|
"@wdio/globals": "8.24.12",
|
||||||
"@wdio/logger": "8.16.17",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/types": "8.24.0",
|
"@wdio/types": "8.24.12",
|
||||||
"@wdio/utils": "8.24.0",
|
"@wdio/utils": "8.24.12",
|
||||||
"deepmerge-ts": "^5.0.0",
|
"deepmerge-ts": "^5.0.0",
|
||||||
"expect-webdriverio": "^4.6.1",
|
"expect-webdriverio": "^4.6.1",
|
||||||
"gaze": "^1.1.2",
|
"gaze": "^1.1.2",
|
||||||
"webdriver": "8.24.0",
|
"webdriver": "8.24.12",
|
||||||
"webdriverio": "8.24.1"
|
"webdriverio": "8.24.12"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.13 || >=18"
|
"node": "^16.13 || >=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/spec-reporter": {
|
"node_modules/@wdio/spec-reporter": {
|
||||||
"version": "8.24.0",
|
"version": "8.24.12",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.24.0.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.24.12.tgz",
|
||||||
"integrity": "sha512-L65ua0+lGkmiiElHWE1zD3EnzZzyJli5tLmwYcahh4LwJ3hEOL7ut/GOZ8LTMih87T1q+KttQiJVmgnOEjMH5w==",
|
"integrity": "sha512-Ng3ErWK8eESamCYwIr2Uv49+46RvmT8FnmGaJ6irJoAp101K8zENEs1pyqYHJReucN+ka/wM87blfc2k8NEHCA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wdio/reporter": "8.24.0",
|
"@wdio/reporter": "8.24.12",
|
||||||
"@wdio/types": "8.24.0",
|
"@wdio/types": "8.24.12",
|
||||||
"chalk": "^5.1.2",
|
"chalk": "^5.1.2",
|
||||||
"easy-table": "^1.2.0",
|
"easy-table": "^1.2.0",
|
||||||
"pretty-ms": "^7.0.0"
|
"pretty-ms": "^7.0.0"
|
||||||
|
@ -1369,9 +1368,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/types": {
|
"node_modules/@wdio/types": {
|
||||||
"version": "8.24.0",
|
"version": "8.24.12",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.24.0.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.24.12.tgz",
|
||||||
"integrity": "sha512-FXbJnQCS1b39RKqBlW9HTNEP4vukxjFc+GiwvPS+XPtY+3Vn7eOyBv3X3CiH1K7C+tzelqlio/HgP68pV5cXsQ==",
|
"integrity": "sha512-SaD3OacDiW06DvSgAQ7sDBbpiI9qZRg7eoVYeBg3uSGVtUq84vTETRhhV7D6xTC00IqZu+mmN2TY5/q+7Gqy7w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.0"
|
"@types/node": "^20.1.0"
|
||||||
|
@ -1381,21 +1380,20 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/utils": {
|
"node_modules/@wdio/utils": {
|
||||||
"version": "8.24.0",
|
"version": "8.24.12",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.24.0.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.24.12.tgz",
|
||||||
"integrity": "sha512-m0qsWx2U5ZBTS0vzg1gTBp9mTrcLQlDrOBVR28LJ93a/e0bj+4aQ4c5U2y9gUzV+lKH0wUJSZTLnhebQwapURQ==",
|
"integrity": "sha512-uzwZyBVgqz0Wz1KL3aOUaQsxT8TNkzxti4NNTSMrU256qAPqc/n75rB7V73QASapCMpy70mZZTsuPgQYYj4ytQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@puppeteer/browsers": "^1.6.0",
|
"@puppeteer/browsers": "^1.6.0",
|
||||||
"@wdio/logger": "8.16.17",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/types": "8.24.0",
|
"@wdio/types": "8.24.12",
|
||||||
"decamelize": "^6.0.0",
|
"decamelize": "^6.0.0",
|
||||||
"deepmerge-ts": "^5.1.0",
|
"deepmerge-ts": "^5.1.0",
|
||||||
"edgedriver": "^5.3.5",
|
"edgedriver": "^5.3.5",
|
||||||
"geckodriver": "^4.2.0",
|
"geckodriver": "^4.2.0",
|
||||||
"get-port": "^7.0.0",
|
"get-port": "^7.0.0",
|
||||||
"got": "^13.0.0",
|
"import-meta-resolve": "^4.0.0",
|
||||||
"import-meta-resolve": "^3.0.0",
|
|
||||||
"locate-app": "^2.1.0",
|
"locate-app": "^2.1.0",
|
||||||
"safaridriver": "^0.1.0",
|
"safaridriver": "^0.1.0",
|
||||||
"split2": "^4.2.0",
|
"split2": "^4.2.0",
|
||||||
|
@ -2457,129 +2455,10 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/detect-package-manager": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/detect-package-manager/-/detect-package-manager-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-qoHDH6+lMcpJPAScE7+5CYj91W0mxZNXTwZPrCqi1KMk+x+AoQScQ2V1QyqTln1rHU5Haq5fikvOGHv+leKD8A==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"execa": "^5.1.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/detect-package-manager/node_modules/execa": {
|
|
||||||
"version": "5.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
|
|
||||||
"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"cross-spawn": "^7.0.3",
|
|
||||||
"get-stream": "^6.0.0",
|
|
||||||
"human-signals": "^2.1.0",
|
|
||||||
"is-stream": "^2.0.0",
|
|
||||||
"merge-stream": "^2.0.0",
|
|
||||||
"npm-run-path": "^4.0.1",
|
|
||||||
"onetime": "^5.1.2",
|
|
||||||
"signal-exit": "^3.0.3",
|
|
||||||
"strip-final-newline": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sindresorhus/execa?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/detect-package-manager/node_modules/get-stream": {
|
|
||||||
"version": "6.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
|
||||||
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/detect-package-manager/node_modules/human-signals": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.17.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/detect-package-manager/node_modules/is-stream": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/detect-package-manager/node_modules/mimic-fn": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/detect-package-manager/node_modules/npm-run-path": {
|
|
||||||
"version": "4.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
|
|
||||||
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"path-key": "^3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/detect-package-manager/node_modules/onetime": {
|
|
||||||
"version": "5.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
|
||||||
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"mimic-fn": "^2.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/detect-package-manager/node_modules/signal-exit": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/detect-package-manager/node_modules/strip-final-newline": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/devtools-protocol": {
|
"node_modules/devtools-protocol": {
|
||||||
"version": "0.0.1213968",
|
"version": "0.0.1233178",
|
||||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1213968.tgz",
|
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1233178.tgz",
|
||||||
"integrity": "sha512-o4n/beY+3CcZwFctYapjGelKptR4AuQT5gXS1Kvgbig+ArwkxK7f8wDVuD1wsoswiJWCwV6OK+Qb7vhNzNmABQ==",
|
"integrity": "sha512-jmMfyaqlzddwmDaSR1AQ+5ek+f7rupZdxKuPdkRcoxrZoF70Idg/4dTgXA08TLPmwAwB54gh49Wm2l/gRM0eUg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/diff": {
|
"node_modules/diff": {
|
||||||
|
@ -2927,15 +2806,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "8.54.0",
|
"version": "8.55.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz",
|
||||||
"integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==",
|
"integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.6.1",
|
"@eslint-community/regexpp": "^4.6.1",
|
||||||
"@eslint/eslintrc": "^2.1.3",
|
"@eslint/eslintrc": "^2.1.4",
|
||||||
"@eslint/js": "8.54.0",
|
"@eslint/js": "8.55.0",
|
||||||
"@humanwhocodes/config-array": "^0.11.13",
|
"@humanwhocodes/config-array": "^0.11.13",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
"@nodelib/fs.walk": "^1.2.8",
|
"@nodelib/fs.walk": "^1.2.8",
|
||||||
|
@ -4008,43 +3887,6 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/got": {
|
|
||||||
"version": "13.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz",
|
|
||||||
"integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@sindresorhus/is": "^5.2.0",
|
|
||||||
"@szmarczak/http-timer": "^5.0.1",
|
|
||||||
"cacheable-lookup": "^7.0.0",
|
|
||||||
"cacheable-request": "^10.2.8",
|
|
||||||
"decompress-response": "^6.0.0",
|
|
||||||
"form-data-encoder": "^2.1.2",
|
|
||||||
"get-stream": "^6.0.1",
|
|
||||||
"http2-wrapper": "^2.1.10",
|
|
||||||
"lowercase-keys": "^3.0.0",
|
|
||||||
"p-cancelable": "^3.0.0",
|
|
||||||
"responselike": "^3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sindresorhus/got?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/got/node_modules/get-stream": {
|
|
||||||
"version": "6.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
|
||||||
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/graceful-fs": {
|
"node_modules/graceful-fs": {
|
||||||
"version": "4.2.11",
|
"version": "4.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
|
@ -4271,9 +4113,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/import-meta-resolve": {
|
"node_modules/import-meta-resolve": {
|
||||||
"version": "3.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz",
|
||||||
"integrity": "sha512-4IwhLhNNA8yy445rPjD/lWh++7hMDOml2eHtd58eG7h+qK3EryMuuRbsHGPikCoAgIkkDnckKfWSk2iDla/ejg==",
|
"integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
|
@ -8355,9 +8197,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.3.2",
|
"version": "5.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
||||||
"integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==",
|
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
|
@ -8616,20 +8458,20 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/webdriver": {
|
"node_modules/webdriver": {
|
||||||
"version": "8.24.0",
|
"version": "8.24.12",
|
||||||
"resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.24.0.tgz",
|
"resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.24.12.tgz",
|
||||||
"integrity": "sha512-zI1zw4lbP2cg1NPikIaUBHQU3+xdvEEBi0Jrydhtp3VVeIEqJWwUFxG/P9LwJpiQ0PYMb/5cxoQrSRhrEXyXHQ==",
|
"integrity": "sha512-03DQIClHoaAqTsmDkxGwo4HwHfkn9LzJ1wfNyUerzKg8DnyXeiT6ILqj6EXLfsvh5zddU2vhYGLFXSerPgkuOQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.0",
|
"@types/node": "^20.1.0",
|
||||||
"@types/ws": "^8.5.3",
|
"@types/ws": "^8.5.3",
|
||||||
"@wdio/config": "8.24.0",
|
"@wdio/config": "8.24.12",
|
||||||
"@wdio/logger": "8.16.17",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/protocols": "8.23.0",
|
"@wdio/protocols": "8.24.12",
|
||||||
"@wdio/types": "8.24.0",
|
"@wdio/types": "8.24.12",
|
||||||
"@wdio/utils": "8.24.0",
|
"@wdio/utils": "8.24.12",
|
||||||
"deepmerge-ts": "^5.1.0",
|
"deepmerge-ts": "^5.1.0",
|
||||||
"got": "^ 12.6.1",
|
"got": "^12.6.1",
|
||||||
"ky": "^0.33.0",
|
"ky": "^0.33.0",
|
||||||
"ws": "^8.8.0"
|
"ws": "^8.8.0"
|
||||||
},
|
},
|
||||||
|
@ -8675,25 +8517,25 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/webdriverio": {
|
"node_modules/webdriverio": {
|
||||||
"version": "8.24.1",
|
"version": "8.24.12",
|
||||||
"resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.24.1.tgz",
|
"resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.24.12.tgz",
|
||||||
"integrity": "sha512-NMu5Y0EFjx7GK4K8uDDi14q8IdHdSQiqzJoyGjuzGy8mj5c04Ta1hoLG5KPag5LzIQNOtJmqwbTFL5PLqragOg==",
|
"integrity": "sha512-Ddu0NNRMVkTzRzqvm3m0wt2eLUn+Plz2Cj+1QXDnVpddYJvk9J3elZC2hqNyscEtecQ+h2y3r36OcJqkl9jPag==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.0",
|
"@types/node": "^20.1.0",
|
||||||
"@wdio/config": "8.24.0",
|
"@wdio/config": "8.24.12",
|
||||||
"@wdio/logger": "8.16.17",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/protocols": "8.23.0",
|
"@wdio/protocols": "8.24.12",
|
||||||
"@wdio/repl": "8.23.1",
|
"@wdio/repl": "8.24.12",
|
||||||
"@wdio/types": "8.24.0",
|
"@wdio/types": "8.24.12",
|
||||||
"@wdio/utils": "8.24.0",
|
"@wdio/utils": "8.24.12",
|
||||||
"archiver": "^6.0.0",
|
"archiver": "^6.0.0",
|
||||||
"aria-query": "^5.0.0",
|
"aria-query": "^5.0.0",
|
||||||
"css-shorthand-properties": "^1.1.1",
|
"css-shorthand-properties": "^1.1.1",
|
||||||
"css-value": "^0.0.1",
|
"css-value": "^0.0.1",
|
||||||
"devtools-protocol": "^0.0.1213968",
|
"devtools-protocol": "^0.0.1233178",
|
||||||
"grapheme-splitter": "^1.0.2",
|
"grapheme-splitter": "^1.0.2",
|
||||||
"import-meta-resolve": "^3.0.0",
|
"import-meta-resolve": "^4.0.0",
|
||||||
"is-plain-obj": "^4.1.0",
|
"is-plain-obj": "^4.1.0",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"lodash.zip": "^4.2.0",
|
"lodash.zip": "^4.2.0",
|
||||||
|
@ -8703,7 +8545,7 @@
|
||||||
"resq": "^1.9.1",
|
"resq": "^1.9.1",
|
||||||
"rgb2hex": "0.2.5",
|
"rgb2hex": "0.2.5",
|
||||||
"serialize-error": "^11.0.1",
|
"serialize-error": "^11.0.1",
|
||||||
"webdriver": "8.24.0"
|
"webdriver": "8.24.12"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.13 || >=18"
|
"node": "^16.13 || >=18"
|
||||||
|
|
|
@ -4,19 +4,19 @@
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
"@typescript-eslint/eslint-plugin": "^6.13.2",
|
||||||
"@typescript-eslint/parser": "^6.12.0",
|
"@typescript-eslint/parser": "^6.13.2",
|
||||||
"@wdio/cli": "^8.24.1",
|
"@wdio/cli": "^8.24.16",
|
||||||
"@wdio/local-runner": "^8.24.1",
|
"@wdio/local-runner": "^8.24.12",
|
||||||
"@wdio/mocha-framework": "^8.24.0",
|
"@wdio/mocha-framework": "^8.24.12",
|
||||||
"@wdio/spec-reporter": "^8.24.0",
|
"@wdio/spec-reporter": "^8.24.12",
|
||||||
"eslint": "^8.54.0",
|
"eslint": "^8.55.0",
|
||||||
"eslint-config-google": "^0.14.0",
|
"eslint-config-google": "^0.14.0",
|
||||||
"eslint-plugin-sonarjs": "^0.23.0",
|
"eslint-plugin-sonarjs": "^0.23.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^3.1.0",
|
"prettier": "^3.1.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^5.3.2",
|
"typescript": "^5.3.3",
|
||||||
"wdio-wait-for": "^3.0.9"
|
"wdio-wait-for": "^3.0.9"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -30,31 +30,31 @@
|
||||||
"storybook:build": "cross-env NODE_OPTIONS='--max_old_space_size=4096' storybook build"
|
"storybook:build": "cross-env NODE_OPTIONS='--max_old_space_size=4096' storybook build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/lang-html": "^6.4.6",
|
"@codemirror/lang-html": "^6.4.7",
|
||||||
"@codemirror/lang-javascript": "^6.2.1",
|
"@codemirror/lang-javascript": "^6.2.1",
|
||||||
"@codemirror/lang-python": "^6.1.3",
|
"@codemirror/lang-python": "^6.1.3",
|
||||||
"@codemirror/lang-xml": "^6.0.2",
|
"@codemirror/lang-xml": "^6.0.2",
|
||||||
"@codemirror/legacy-modes": "^6.3.3",
|
"@codemirror/legacy-modes": "^6.3.3",
|
||||||
"@codemirror/theme-one-dark": "^6.1.2",
|
"@codemirror/theme-one-dark": "^6.1.2",
|
||||||
"@formatjs/intl-listformat": "^7.5.3",
|
"@formatjs/intl-listformat": "^7.5.3",
|
||||||
"@fortawesome/fontawesome-free": "^6.4.2",
|
"@fortawesome/fontawesome-free": "^6.5.1",
|
||||||
"@goauthentik/api": "^2023.10.4-1700591367",
|
"@goauthentik/api": "^2023.10.4-1701882394",
|
||||||
"@lit-labs/context": "^0.4.0",
|
"@lit-labs/context": "^0.4.0",
|
||||||
"@lit-labs/task": "^3.1.0",
|
"@lit-labs/task": "^3.1.0",
|
||||||
"@lit/localize": "^0.11.4",
|
"@lit/localize": "^0.11.4",
|
||||||
"@open-wc/lit-helpers": "^0.6.0",
|
"@open-wc/lit-helpers": "^0.6.0",
|
||||||
"@patternfly/elements": "^2.4.0",
|
"@patternfly/elements": "^2.4.0",
|
||||||
"@patternfly/patternfly": "^4.224.2",
|
"@patternfly/patternfly": "^4.224.2",
|
||||||
"@sentry/browser": "^7.81.1",
|
"@sentry/browser": "^7.86.0",
|
||||||
"@sentry/tracing": "^7.81.1",
|
"@sentry/tracing": "^7.86.0",
|
||||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"chart.js": "^4.4.0",
|
"chart.js": "^4.4.1",
|
||||||
"chartjs-adapter-moment": "^1.0.1",
|
"chartjs-adapter-moment": "^1.0.1",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"construct-style-sheets-polyfill": "^3.1.0",
|
"construct-style-sheets-polyfill": "^3.1.0",
|
||||||
"core-js": "^3.33.3",
|
"core-js": "^3.34.0",
|
||||||
"country-flag-icons": "^1.5.7",
|
"country-flag-icons": "^1.5.9",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"lit": "^2.8.0",
|
"lit": "^2.8.0",
|
||||||
"mermaid": "^10.6.1",
|
"mermaid": "^10.6.1",
|
||||||
|
@ -64,13 +64,13 @@
|
||||||
"yaml": "^2.3.4"
|
"yaml": "^2.3.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.23.3",
|
"@babel/core": "^7.23.5",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||||
"@babel/plugin-proposal-decorators": "^7.23.3",
|
"@babel/plugin-proposal-decorators": "^7.23.5",
|
||||||
"@babel/plugin-transform-private-methods": "^7.23.3",
|
"@babel/plugin-transform-private-methods": "^7.23.3",
|
||||||
"@babel/plugin-transform-private-property-in-object": "^7.23.4",
|
"@babel/plugin-transform-private-property-in-object": "^7.23.4",
|
||||||
"@babel/plugin-transform-runtime": "^7.23.4",
|
"@babel/plugin-transform-runtime": "^7.23.4",
|
||||||
"@babel/preset-env": "^7.23.3",
|
"@babel/preset-env": "^7.23.5",
|
||||||
"@babel/preset-typescript": "^7.23.3",
|
"@babel/preset-typescript": "^7.23.3",
|
||||||
"@hcaptcha/types": "^1.0.3",
|
"@hcaptcha/types": "^1.0.3",
|
||||||
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
|
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
|
||||||
|
@ -82,21 +82,23 @@
|
||||||
"@rollup/plugin-replace": "^5.0.5",
|
"@rollup/plugin-replace": "^5.0.5",
|
||||||
"@rollup/plugin-terser": "^0.4.4",
|
"@rollup/plugin-terser": "^0.4.4",
|
||||||
"@rollup/plugin-typescript": "^11.1.5",
|
"@rollup/plugin-typescript": "^11.1.5",
|
||||||
"@storybook/addon-essentials": "^7.5.3",
|
"@storybook/addon-essentials": "^7.6.4",
|
||||||
"@storybook/addon-links": "^7.5.3",
|
"@storybook/addon-links": "^7.6.4",
|
||||||
"@storybook/blocks": "^7.1.1",
|
"@storybook/api": "^7.6.4",
|
||||||
"@storybook/web-components": "^7.5.3",
|
"@storybook/blocks": "^7.6.4",
|
||||||
"@storybook/web-components-vite": "^7.5.3",
|
"@storybook/manager-api": "^7.6.4",
|
||||||
|
"@storybook/web-components-vite": "^7.6.4",
|
||||||
|
"@storybook/web-components": "^7.6.4",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||||
"@types/chart.js": "^2.9.41",
|
"@types/chart.js": "^2.9.41",
|
||||||
"@types/codemirror": "5.60.15",
|
"@types/codemirror": "5.60.15",
|
||||||
"@types/grecaptcha": "^3.0.7",
|
"@types/grecaptcha": "^3.0.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
"@typescript-eslint/eslint-plugin": "^6.13.2",
|
||||||
"@typescript-eslint/parser": "^6.12.0",
|
"@typescript-eslint/parser": "^6.13.2",
|
||||||
"babel-plugin-macros": "^3.1.0",
|
"babel-plugin-macros": "^3.1.0",
|
||||||
"babel-plugin-tsconfig-paths": "^1.0.3",
|
"babel-plugin-tsconfig-paths": "^1.0.3",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.54.0",
|
"eslint": "^8.55.0",
|
||||||
"eslint-config-google": "^0.14.0",
|
"eslint-config-google": "^0.14.0",
|
||||||
"eslint-plugin-custom-elements": "0.0.8",
|
"eslint-plugin-custom-elements": "0.0.8",
|
||||||
"eslint-plugin-lit": "^1.10.1",
|
"eslint-plugin-lit": "^1.10.1",
|
||||||
|
@ -106,25 +108,25 @@
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^3.1.0",
|
"prettier": "^3.1.0",
|
||||||
"pseudolocale": "^2.0.0",
|
"pseudolocale": "^2.0.0",
|
||||||
"pyright": "^1.1.337",
|
"pyright": "^1.1.338",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"rollup": "^4.5.1",
|
"rollup": "^4.6.1",
|
||||||
"rollup-plugin-copy": "^3.5.0",
|
"rollup-plugin-copy": "^3.5.0",
|
||||||
"rollup-plugin-cssimport": "^1.0.3",
|
"rollup-plugin-cssimport": "^1.0.3",
|
||||||
"rollup-plugin-postcss-lit": "^2.1.0",
|
"rollup-plugin-postcss-lit": "^2.1.0",
|
||||||
"storybook": "^7.5.3",
|
"storybook": "^7.6.4",
|
||||||
"storybook-addon-mock": "^4.3.0",
|
"storybook-addon-mock": "^4.3.0",
|
||||||
"ts-lit-plugin": "^2.0.1",
|
"ts-lit-plugin": "^2.0.1",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.6.2",
|
||||||
"turnstile-types": "^1.1.3",
|
"turnstile-types": "^1.1.3",
|
||||||
"typescript": "^5.3.2",
|
"typescript": "^5.3.3",
|
||||||
"vite-tsconfig-paths": "^4.2.1"
|
"vite-tsconfig-paths": "^4.2.2"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@esbuild/darwin-arm64": "^0.19.7",
|
"@esbuild/darwin-arm64": "^0.19.8",
|
||||||
"@esbuild/linux-amd64": "^0.18.11",
|
"@esbuild/linux-amd64": "^0.18.11",
|
||||||
"@esbuild/linux-arm64": "^0.19.7"
|
"@esbuild/linux-arm64": "^0.19.8"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
|
|
|
@ -89,17 +89,15 @@ export class ApplicationListPage extends TablePage<Application> {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderSectionBefore(): TemplateResult {
|
||||||
|
return html`<ak-application-wizard-hint></ak-application-wizard-hint>`;
|
||||||
|
}
|
||||||
|
|
||||||
renderSidebarAfter(): TemplateResult {
|
renderSidebarAfter(): TemplateResult {
|
||||||
// Rendering the wizard with .open here, as if we set the attribute in
|
// Rendering the wizard with .open here, as if we set the attribute in
|
||||||
// renderObjectCreate() it'll open two wizards, since that function gets called twice
|
// renderObjectCreate() it'll open two wizards, since that function gets called twice
|
||||||
|
|
||||||
/* Re-enable the wizard later:
|
return html`<div class="pf-c-sidebar__panel pf-m-width-25">
|
||||||
<ak-application-wizard
|
|
||||||
.open=${getURLParam("createWizard", false)}
|
|
||||||
.showButton=${false}
|
|
||||||
></ak-application-wizard>*/
|
|
||||||
|
|
||||||
return html` <div class="pf-c-sidebar__panel pf-m-width-25">
|
|
||||||
<div class="pf-c-card">
|
<div class="pf-c-card">
|
||||||
<div class="pf-c-card__body">
|
<div class="pf-c-card__body">
|
||||||
<ak-markdown .md=${MDApplication}></ak-markdown>
|
<ak-markdown .md=${MDApplication}></ak-markdown>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { WizardPanel } from "@goauthentik/components/ak-wizard-main/types";
|
import { WizardPanel } from "@goauthentik/components/ak-wizard-main/types";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
|
import { KeyUnknown, serializeForm } from "@goauthentik/elements/forms/Form";
|
||||||
|
import { HorizontalFormElement } from "@goauthentik/elements/forms/HorizontalFormElement";
|
||||||
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
|
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
|
||||||
|
|
||||||
import { consume } from "@lit-labs/context";
|
import { consume } from "@lit-labs/context";
|
||||||
|
@ -10,6 +12,15 @@ import { styles as AwadStyles } from "./BasePanel.css";
|
||||||
import { applicationWizardContext } from "./ContextIdentity";
|
import { applicationWizardContext } from "./ContextIdentity";
|
||||||
import type { ApplicationWizardState, ApplicationWizardStateUpdate } from "./types";
|
import type { ApplicationWizardState, ApplicationWizardStateUpdate } from "./types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application Wizard Base Panel
|
||||||
|
*
|
||||||
|
* All of the displays in our system inherit from this object, which supplies the basic CSS for all
|
||||||
|
* the inputs we display, as well as the values and validity state for the form currently being
|
||||||
|
* displayed.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
export class ApplicationWizardPageBase
|
export class ApplicationWizardPageBase
|
||||||
extends CustomEmitterElement(AKElement)
|
extends CustomEmitterElement(AKElement)
|
||||||
implements WizardPanel
|
implements WizardPanel
|
||||||
|
@ -18,15 +29,41 @@ export class ApplicationWizardPageBase
|
||||||
return AwadStyles;
|
return AwadStyles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@query("form")
|
|
||||||
form!: HTMLFormElement;
|
|
||||||
|
|
||||||
rendered = false;
|
|
||||||
|
|
||||||
@consume({ context: applicationWizardContext })
|
@consume({ context: applicationWizardContext })
|
||||||
public wizard!: ApplicationWizardState;
|
public wizard!: ApplicationWizardState;
|
||||||
|
|
||||||
// This used to be more complex; now it just establishes the event name.
|
@query("form")
|
||||||
|
form!: HTMLFormElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide access to the values on the current form. Child implementations use this to craft the
|
||||||
|
* update that will be sent using `dispatchWizardUpdate` below.
|
||||||
|
*/
|
||||||
|
get formValues(): KeyUnknown | undefined {
|
||||||
|
const elements = [
|
||||||
|
...Array.from(
|
||||||
|
this.form.querySelectorAll<HorizontalFormElement>("ak-form-element-horizontal"),
|
||||||
|
),
|
||||||
|
...Array.from(this.form.querySelectorAll<HTMLElement>("[data-ak-control=true]")),
|
||||||
|
];
|
||||||
|
return serializeForm(elements as unknown as NodeListOf<HorizontalFormElement>);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide access to the validity of the current form. Child implementations use this to craft
|
||||||
|
* the update that will be sent using `dispatchWizardUpdate` below.
|
||||||
|
*/
|
||||||
|
get valid() {
|
||||||
|
return this.form.checkValidity();
|
||||||
|
}
|
||||||
|
|
||||||
|
rendered = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a single source of truth for the token used to notify the orchestrator that an event
|
||||||
|
* happens. The token `ak-wizard-update` is used by the Wizard framework's reactive controller
|
||||||
|
* to route "data on the current step has changed" events to the orchestrator.
|
||||||
|
*/
|
||||||
dispatchWizardUpdate(update: ApplicationWizardStateUpdate) {
|
dispatchWizardUpdate(update: ApplicationWizardStateUpdate) {
|
||||||
this.dispatchCustomEvent("ak-wizard-update", update);
|
this.dispatchCustomEvent("ak-wizard-update", update);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,5 +5,3 @@ import { ApplicationWizardState } from "./types";
|
||||||
export const applicationWizardContext = createContext<ApplicationWizardState>(
|
export const applicationWizardContext = createContext<ApplicationWizardState>(
|
||||||
Symbol("ak-application-wizard-state-context"),
|
Symbol("ak-application-wizard-state-context"),
|
||||||
);
|
);
|
||||||
|
|
||||||
export default applicationWizardContext;
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { merge } from "@goauthentik/common/merge";
|
|
||||||
import { AkWizard } from "@goauthentik/components/ak-wizard-main/AkWizard";
|
import { AkWizard } from "@goauthentik/components/ak-wizard-main/AkWizard";
|
||||||
import { CustomListenerElement } from "@goauthentik/elements/utils/eventEmitter";
|
import { CustomListenerElement } from "@goauthentik/elements/utils/eventEmitter";
|
||||||
|
|
||||||
|
@ -6,7 +5,7 @@ import { ContextProvider } from "@lit-labs/context";
|
||||||
import { msg } from "@lit/localize";
|
import { msg } from "@lit/localize";
|
||||||
import { customElement, state } from "lit/decorators.js";
|
import { customElement, state } from "lit/decorators.js";
|
||||||
|
|
||||||
import applicationWizardContext from "./ContextIdentity";
|
import { applicationWizardContext } from "./ContextIdentity";
|
||||||
import { newSteps } from "./steps";
|
import { newSteps } from "./steps";
|
||||||
import {
|
import {
|
||||||
ApplicationStep,
|
ApplicationStep,
|
||||||
|
@ -15,10 +14,11 @@ import {
|
||||||
OneOfProvider,
|
OneOfProvider,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
const freshWizardState = () => ({
|
const freshWizardState = (): ApplicationWizardState => ({
|
||||||
providerModel: "",
|
providerModel: "",
|
||||||
app: {},
|
app: {},
|
||||||
provider: {},
|
provider: {},
|
||||||
|
errors: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
@customElement("ak-application-wizard")
|
@customElement("ak-application-wizard")
|
||||||
|
@ -56,28 +56,6 @@ export class ApplicationWizard extends CustomListenerElement(
|
||||||
*/
|
*/
|
||||||
providerCache: Map<string, OneOfProvider> = new Map();
|
providerCache: Map<string, OneOfProvider> = new Map();
|
||||||
|
|
||||||
maybeProviderSwap(providerModel: string | undefined): boolean {
|
|
||||||
if (
|
|
||||||
providerModel === undefined ||
|
|
||||||
typeof providerModel !== "string" ||
|
|
||||||
providerModel === this.wizardState.providerModel
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.providerCache.set(this.wizardState.providerModel, this.wizardState.provider);
|
|
||||||
const prevProvider = this.providerCache.get(providerModel);
|
|
||||||
this.wizardState.provider = prevProvider ?? {
|
|
||||||
name: `Provider for ${this.wizardState.app.name}`,
|
|
||||||
};
|
|
||||||
const method = this.steps.find(({ id }) => id === "provider-details");
|
|
||||||
if (!method) {
|
|
||||||
throw new Error("Could not find Authentication Method page?");
|
|
||||||
}
|
|
||||||
method.disabled = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// And this is where all the special cases go...
|
// And this is where all the special cases go...
|
||||||
handleUpdate(detail: ApplicationWizardStateUpdate) {
|
handleUpdate(detail: ApplicationWizardStateUpdate) {
|
||||||
if (detail.status === "submitted") {
|
if (detail.status === "submitted") {
|
||||||
|
@ -87,17 +65,26 @@ export class ApplicationWizard extends CustomListenerElement(
|
||||||
}
|
}
|
||||||
|
|
||||||
this.step.valid = this.step.valid || detail.status === "valid";
|
this.step.valid = this.step.valid || detail.status === "valid";
|
||||||
|
|
||||||
const update = detail.update;
|
const update = detail.update;
|
||||||
|
|
||||||
if (!update) {
|
if (!update) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.maybeProviderSwap(update.providerModel)) {
|
// When the providerModel enum changes, retrieve the customer's prior work for *this* wizard
|
||||||
this.requestUpdate();
|
// session (and only this wizard session) or provide an empty model with a default provider
|
||||||
|
// name.
|
||||||
|
if (update.providerModel && update.providerModel !== this.wizardState.providerModel) {
|
||||||
|
const requestedProvider = this.providerCache.get(update.providerModel) ?? {
|
||||||
|
name: `Provider for ${this.wizardState.app.name}`,
|
||||||
|
};
|
||||||
|
if (this.wizardState.providerModel) {
|
||||||
|
this.providerCache.set(this.wizardState.providerModel, this.wizardState.provider);
|
||||||
|
}
|
||||||
|
update.provider = requestedProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.wizardState = merge(this.wizardState, update) as ApplicationWizardState;
|
this.wizardState = update as ApplicationWizardState;
|
||||||
this.wizardStateProvider.setValue(this.wizardState);
|
this.wizardStateProvider.setValue(this.wizardState);
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
|
|
||||||
|
import { css, html } from "lit";
|
||||||
|
import { customElement } from "lit/decorators.js";
|
||||||
|
|
||||||
|
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||||
|
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||||
|
|
||||||
|
@customElement("ak-wizard-title")
|
||||||
|
export class AkWizardTitle extends AKElement {
|
||||||
|
static get styles() {
|
||||||
|
return [
|
||||||
|
PFContent,
|
||||||
|
PFTitle,
|
||||||
|
css`
|
||||||
|
.ak-bottom-spacing {
|
||||||
|
padding-bottom: var(--pf-global--spacer--lg);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`<div class="ak-bottom-spacing pf-c-content">
|
||||||
|
<h3><slot></slot></h3>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AkWizardTitle;
|
|
@ -5,7 +5,6 @@ import "@goauthentik/components/ak-slug-input";
|
||||||
import "@goauthentik/components/ak-switch-input";
|
import "@goauthentik/components/ak-switch-input";
|
||||||
import "@goauthentik/components/ak-text-input";
|
import "@goauthentik/components/ak-text-input";
|
||||||
import "@goauthentik/elements/forms/FormGroup";
|
import "@goauthentik/elements/forms/FormGroup";
|
||||||
import "@goauthentik/elements/forms/FormGroup";
|
|
||||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||||
|
|
||||||
import { msg } from "@lit/localize";
|
import { msg } from "@lit/localize";
|
||||||
|
@ -17,28 +16,20 @@ import BasePanel from "../BasePanel";
|
||||||
|
|
||||||
@customElement("ak-application-wizard-application-details")
|
@customElement("ak-application-wizard-application-details")
|
||||||
export class ApplicationWizardApplicationDetails extends BasePanel {
|
export class ApplicationWizardApplicationDetails extends BasePanel {
|
||||||
handleChange(ev: Event) {
|
handleChange(_ev: Event) {
|
||||||
if (!ev.target) {
|
const formValues = this.formValues;
|
||||||
console.warn(`Received event with no target: ${ev}`);
|
if (!formValues) {
|
||||||
return;
|
throw new Error("No application values on form?");
|
||||||
}
|
}
|
||||||
|
|
||||||
const target = ev.target as HTMLInputElement;
|
|
||||||
const value = target.type === "checkbox" ? target.checked : target.value;
|
|
||||||
this.dispatchWizardUpdate({
|
this.dispatchWizardUpdate({
|
||||||
update: {
|
update: {
|
||||||
app: {
|
...this.wizard,
|
||||||
[target.name]: value,
|
app: formValues,
|
||||||
},
|
},
|
||||||
},
|
status: this.valid ? "valid" : "invalid",
|
||||||
status: this.form.checkValidity() ? "valid" : "invalid",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
validator() {
|
|
||||||
return this.form.reportValidity();
|
|
||||||
}
|
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
return html` <form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
return html` <form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
|
@ -48,6 +39,7 @@ export class ApplicationWizardApplicationDetails extends BasePanel {
|
||||||
required
|
required
|
||||||
help=${msg("Application's display Name.")}
|
help=${msg("Application's display Name.")}
|
||||||
id="ak-application-wizard-details-name"
|
id="ak-application-wizard-details-name"
|
||||||
|
.errorMessages=${this.wizard.errors.app?.name ?? []}
|
||||||
></ak-text-input>
|
></ak-text-input>
|
||||||
<ak-slug-input
|
<ak-slug-input
|
||||||
name="slug"
|
name="slug"
|
||||||
|
@ -56,11 +48,13 @@ export class ApplicationWizardApplicationDetails extends BasePanel {
|
||||||
source="#ak-application-wizard-details-name"
|
source="#ak-application-wizard-details-name"
|
||||||
required
|
required
|
||||||
help=${msg("Internal application name used in URLs.")}
|
help=${msg("Internal application name used in URLs.")}
|
||||||
|
.errorMessages=${this.wizard.errors.app?.slug ?? []}
|
||||||
></ak-slug-input>
|
></ak-slug-input>
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
name="group"
|
name="group"
|
||||||
value=${ifDefined(this.wizard.app?.group)}
|
value=${ifDefined(this.wizard.app?.group)}
|
||||||
label=${msg("Group")}
|
label=${msg("Group")}
|
||||||
|
.errorMessages=${this.wizard.errors.app?.group ?? []}
|
||||||
help=${msg(
|
help=${msg(
|
||||||
"Optionally enter a group name. Applications with identical groups are shown grouped together.",
|
"Optionally enter a group name. Applications with identical groups are shown grouped together.",
|
||||||
)}
|
)}
|
||||||
|
@ -71,6 +65,7 @@ export class ApplicationWizardApplicationDetails extends BasePanel {
|
||||||
name="policyEngineMode"
|
name="policyEngineMode"
|
||||||
.options=${policyOptions}
|
.options=${policyOptions}
|
||||||
.value=${this.wizard.app?.policyEngineMode}
|
.value=${this.wizard.app?.policyEngineMode}
|
||||||
|
.errorMessages=${this.wizard.errors.app?.policyEngineMode ?? []}
|
||||||
></ak-radio-input>
|
></ak-radio-input>
|
||||||
<ak-form-group aria-label="UI Settings">
|
<ak-form-group aria-label="UI Settings">
|
||||||
<span slot="header"> ${msg("UI Settings")} </span>
|
<span slot="header"> ${msg("UI Settings")} </span>
|
||||||
|
@ -82,6 +77,7 @@ export class ApplicationWizardApplicationDetails extends BasePanel {
|
||||||
help=${msg(
|
help=${msg(
|
||||||
"If left empty, authentik will try to extract the launch URL based on the selected provider.",
|
"If left empty, authentik will try to extract the launch URL based on the selected provider.",
|
||||||
)}
|
)}
|
||||||
|
.errorMessages=${this.wizard.errors.app?.metaLaunchUrl ?? []}
|
||||||
></ak-text-input>
|
></ak-text-input>
|
||||||
<ak-switch-input
|
<ak-switch-input
|
||||||
name="openInNewTab"
|
name="openInNewTab"
|
||||||
|
|
|
@ -19,13 +19,17 @@ type ProviderRenderer = () => TemplateResult;
|
||||||
|
|
||||||
type ModelConverter = (provider: OneOfProvider) => ModelRequest;
|
type ModelConverter = (provider: OneOfProvider) => ModelRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There's an internal key and an API key because "Proxy" has three different subtypes.
|
||||||
|
*/
|
||||||
|
// prettier-ignore
|
||||||
type ProviderType = [
|
type ProviderType = [
|
||||||
string,
|
string, // internal key used by the wizard to distinguish between providers
|
||||||
string,
|
string, // Name of the provider
|
||||||
string,
|
string, // Description
|
||||||
ProviderRenderer,
|
ProviderRenderer, // Function that returns the provider's wizard panel as a TemplateResult
|
||||||
ProviderModelEnumType,
|
ProviderModelEnumType, // key used by the API to distinguish between providers
|
||||||
ModelConverter,
|
ModelConverter, // Handler that takes a generic provider and returns one specifically typed to its panel
|
||||||
];
|
];
|
||||||
|
|
||||||
export type LocalTypeCreate = TypeCreate & {
|
export type LocalTypeCreate = TypeCreate & {
|
||||||
|
|
|
@ -25,19 +25,15 @@ export class ApplicationWizardAuthenticationMethodChoice extends BasePanel {
|
||||||
handleChoice(ev: InputEvent) {
|
handleChoice(ev: InputEvent) {
|
||||||
const target = ev.target as HTMLInputElement;
|
const target = ev.target as HTMLInputElement;
|
||||||
this.dispatchWizardUpdate({
|
this.dispatchWizardUpdate({
|
||||||
update: { providerModel: target.value },
|
update: {
|
||||||
status: this.validator() ? "valid" : "invalid",
|
...this.wizard,
|
||||||
|
providerModel: target.value,
|
||||||
|
errors: {},
|
||||||
|
},
|
||||||
|
status: this.valid ? "valid" : "invalid",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
validator() {
|
|
||||||
const radios = Array.from(this.form.querySelectorAll('input[type="radio"]'));
|
|
||||||
const chosen = radios.find(
|
|
||||||
(radio: Element) => radio instanceof HTMLInputElement && radio.checked,
|
|
||||||
);
|
|
||||||
return !!chosen;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderProvider(type: LocalTypeCreate) {
|
renderProvider(type: LocalTypeCreate) {
|
||||||
const method = this.wizard.providerModel;
|
const method = this.wizard.providerModel;
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,14 @@ import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||||
import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";
|
import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ApplicationRequest,
|
type ApplicationRequest,
|
||||||
CoreApi,
|
CoreApi,
|
||||||
TransactionApplicationRequest,
|
type ModelRequest,
|
||||||
TransactionApplicationResponse,
|
type TransactionApplicationRequest,
|
||||||
|
type TransactionApplicationResponse,
|
||||||
|
ValidationError,
|
||||||
|
ValidationErrorFromJSON,
|
||||||
} from "@goauthentik/api";
|
} from "@goauthentik/api";
|
||||||
import type { ModelRequest } from "@goauthentik/api";
|
|
||||||
|
|
||||||
import BasePanel from "../BasePanel";
|
import BasePanel from "../BasePanel";
|
||||||
import providerModelsList from "../auth-method-choice/ak-application-wizard-authentication-method-choice.choices";
|
import providerModelsList from "../auth-method-choice/ak-application-wizard-authentication-method-choice.choices";
|
||||||
|
@ -88,7 +90,7 @@ export class ApplicationWizardCommitApplication extends BasePanel {
|
||||||
commitState: State = idleState;
|
commitState: State = idleState;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
errors: string[] = [];
|
errors?: ValidationError;
|
||||||
|
|
||||||
response?: TransactionApplicationResponse;
|
response?: TransactionApplicationResponse;
|
||||||
|
|
||||||
|
@ -121,27 +123,10 @@ export class ApplicationWizardCommitApplication extends BasePanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
decodeErrors(body: Record<string, any>) {
|
|
||||||
const spaceify = (src: Record<string, string>) =>
|
|
||||||
Object.values(src).map((msg) => `\u00a0\u00a0\u00a0\u00a0${msg}`);
|
|
||||||
|
|
||||||
let errs: string[] = [];
|
|
||||||
if (body["app"] !== undefined) {
|
|
||||||
errs = [...errs, msg("In the Application:"), ...spaceify(body["app"])];
|
|
||||||
}
|
|
||||||
if (body["provider"] !== undefined) {
|
|
||||||
errs = [...errs, msg("In the Provider:"), ...spaceify(body["provider"])];
|
|
||||||
}
|
|
||||||
console.log(body, errs);
|
|
||||||
return errs;
|
|
||||||
}
|
|
||||||
|
|
||||||
async send(
|
async send(
|
||||||
data: TransactionApplicationRequest,
|
data: TransactionApplicationRequest,
|
||||||
): Promise<TransactionApplicationResponse | void> {
|
): Promise<TransactionApplicationResponse | void> {
|
||||||
this.errors = [];
|
this.errors = undefined;
|
||||||
|
|
||||||
new CoreApi(DEFAULT_CONFIG)
|
new CoreApi(DEFAULT_CONFIG)
|
||||||
.coreTransactionalApplicationsUpdate({
|
.coreTransactionalApplicationsUpdate({
|
||||||
transactionApplicationRequest: data,
|
transactionApplicationRequest: data,
|
||||||
|
@ -153,18 +138,57 @@ export class ApplicationWizardCommitApplication extends BasePanel {
|
||||||
this.commitState = successState;
|
this.commitState = successState;
|
||||||
})
|
})
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
.catch((resolution: any) => {
|
.catch(async (resolution: any) => {
|
||||||
resolution.response.json().then(
|
const errors = (this.errors = ValidationErrorFromJSON(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
await resolution.response.json(),
|
||||||
(body: Record<string, any>) => {
|
));
|
||||||
this.errors = this.decodeErrors(body);
|
this.dispatchWizardUpdate({
|
||||||
|
update: {
|
||||||
|
...this.wizard,
|
||||||
|
errors,
|
||||||
},
|
},
|
||||||
);
|
status: "failed",
|
||||||
|
});
|
||||||
this.commitState = errorState;
|
this.commitState = errorState;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): TemplateResult {
|
renderErrors(errors?: ValidationError) {
|
||||||
|
if (!errors) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
const navTo = (step: number) => () =>
|
||||||
|
this.dispatchCustomEvent("ak-wizard-nav", {
|
||||||
|
command: "goto",
|
||||||
|
step,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (errors.app) {
|
||||||
|
return html`<p>${msg("There was an error in the application.")}</p>
|
||||||
|
<p><a @click=${navTo(0)}>${msg("Review the application.")}</a></p>`;
|
||||||
|
}
|
||||||
|
if (errors.provider) {
|
||||||
|
return html`<p>${msg("There was an error in the provider.")}</p>
|
||||||
|
<p><a @click=${navTo(2)}>${msg("Review the provider.")}</a></p>`;
|
||||||
|
}
|
||||||
|
if (errors.detail) {
|
||||||
|
return html`<p>${msg("There was an error")}: ${errors.detail}</p>`;
|
||||||
|
}
|
||||||
|
if ((errors?.nonFieldErrors ?? []).length > 0) {
|
||||||
|
return html`<p>$(msg("There was an error")}:</p>
|
||||||
|
<ul>
|
||||||
|
${(errors.nonFieldErrors ?? []).map((e: string) => html`<li>${e}</li>`)}
|
||||||
|
</ul>`;
|
||||||
|
}
|
||||||
|
return html`<p>
|
||||||
|
${msg(
|
||||||
|
"There was an error creating the application, but no error message was sent. Please review the server logs.",
|
||||||
|
)}
|
||||||
|
</p>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
const icon = classMap(
|
const icon = classMap(
|
||||||
this.commitState.icon.reduce((acc, icon) => ({ ...acc, [icon]: true }), {}),
|
this.commitState.icon.reduce((acc, icon) => ({ ...acc, [icon]: true }), {}),
|
||||||
);
|
);
|
||||||
|
@ -184,13 +208,7 @@ export class ApplicationWizardCommitApplication extends BasePanel {
|
||||||
>
|
>
|
||||||
${this.commitState.label}
|
${this.commitState.label}
|
||||||
</h1>
|
</h1>
|
||||||
${this.errors.length > 0
|
${this.renderErrors(this.errors)}
|
||||||
? html`<ul>
|
|
||||||
${this.errors.map(
|
|
||||||
(msg) => html`<li><code>${msg}</code></li>`,
|
|
||||||
)}
|
|
||||||
</ul>`
|
|
||||||
: nothing}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,26 +1,19 @@
|
||||||
import BasePanel from "../BasePanel";
|
import BasePanel from "../BasePanel";
|
||||||
|
|
||||||
export class ApplicationWizardProviderPageBase extends BasePanel {
|
export class ApplicationWizardProviderPageBase extends BasePanel {
|
||||||
handleChange(ev: InputEvent) {
|
handleChange(_ev: InputEvent) {
|
||||||
if (!ev.target) {
|
const formValues = this.formValues;
|
||||||
console.warn(`Received event with no target: ${ev}`);
|
if (!formValues) {
|
||||||
return;
|
throw new Error("No provider values on form?");
|
||||||
}
|
}
|
||||||
const target = ev.target as HTMLInputElement;
|
|
||||||
const value = target.type === "checkbox" ? target.checked : target.value;
|
|
||||||
this.dispatchWizardUpdate({
|
this.dispatchWizardUpdate({
|
||||||
update: {
|
update: {
|
||||||
provider: {
|
...this.wizard,
|
||||||
[target.name]: value,
|
provider: formValues,
|
||||||
},
|
},
|
||||||
},
|
status: this.valid ? "valid" : "invalid",
|
||||||
status: this.form.checkValidity() ? "valid" : "invalid",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
validator() {
|
|
||||||
return this.form.reportValidity();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ApplicationWizardProviderPageBase;
|
export default ApplicationWizardProviderPageBase;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import "@goauthentik/admin/applications/wizard/ak-wizard-title";
|
||||||
import "@goauthentik/admin/common/ak-core-group-search";
|
import "@goauthentik/admin/common/ak-core-group-search";
|
||||||
import "@goauthentik/admin/common/ak-crypto-certificate-search";
|
import "@goauthentik/admin/common/ak-crypto-certificate-search";
|
||||||
import "@goauthentik/admin/common/ak-flow-search/ak-tenanted-flow-search";
|
import "@goauthentik/admin/common/ak-flow-search/ak-tenanted-flow-search";
|
||||||
|
@ -34,12 +35,15 @@ import {
|
||||||
export class ApplicationWizardApplicationDetails extends BaseProviderPanel {
|
export class ApplicationWizardApplicationDetails extends BaseProviderPanel {
|
||||||
render() {
|
render() {
|
||||||
const provider = this.wizard.provider as LDAPProvider | undefined;
|
const provider = this.wizard.provider as LDAPProvider | undefined;
|
||||||
|
const errors = this.wizard.errors.provider;
|
||||||
|
|
||||||
return html` <form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
return html` <ak-wizard-title>${msg("Configure LDAP Provider")}</ak-wizard-title>
|
||||||
|
<form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
name="name"
|
name="name"
|
||||||
value=${ifDefined(provider?.name)}
|
value=${ifDefined(provider?.name)}
|
||||||
label=${msg("Name")}
|
label=${msg("Name")}
|
||||||
|
.errorMessages=${errors?.name ?? []}
|
||||||
required
|
required
|
||||||
help=${msg("Method's display Name.")}
|
help=${msg("Method's display Name.")}
|
||||||
></ak-text-input>
|
></ak-text-input>
|
||||||
|
@ -48,6 +52,7 @@ export class ApplicationWizardApplicationDetails extends BaseProviderPanel {
|
||||||
label=${msg("Bind flow")}
|
label=${msg("Bind flow")}
|
||||||
?required=${true}
|
?required=${true}
|
||||||
name="authorizationFlow"
|
name="authorizationFlow"
|
||||||
|
.errorMessages=${errors?.authorizationFlow ?? []}
|
||||||
>
|
>
|
||||||
<ak-tenanted-flow-search
|
<ak-tenanted-flow-search
|
||||||
flowType=${FlowsInstancesListDesignationEnum.Authentication}
|
flowType=${FlowsInstancesListDesignationEnum.Authentication}
|
||||||
|
@ -55,10 +60,16 @@ export class ApplicationWizardApplicationDetails extends BaseProviderPanel {
|
||||||
.tenantFlow=${rootInterface()?.tenant?.flowAuthentication}
|
.tenantFlow=${rootInterface()?.tenant?.flowAuthentication}
|
||||||
required
|
required
|
||||||
></ak-tenanted-flow-search>
|
></ak-tenanted-flow-search>
|
||||||
<p class="pf-c-form__helper-text">${msg("Flow used for users to authenticate.")}</p>
|
<p class="pf-c-form__helper-text">
|
||||||
|
${msg("Flow used for users to authenticate.")}
|
||||||
|
</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
|
||||||
<ak-form-element-horizontal label=${msg("Search group")} name="searchGroup">
|
<ak-form-element-horizontal
|
||||||
|
label=${msg("Search group")}
|
||||||
|
name="searchGroup"
|
||||||
|
.errorMessages=${errors?.searchGroup ?? []}
|
||||||
|
>
|
||||||
<ak-core-group-search
|
<ak-core-group-search
|
||||||
name="searchGroup"
|
name="searchGroup"
|
||||||
group=${ifDefined(provider?.searchGroup ?? nothing)}
|
group=${ifDefined(provider?.searchGroup ?? nothing)}
|
||||||
|
@ -80,14 +91,16 @@ export class ApplicationWizardApplicationDetails extends BaseProviderPanel {
|
||||||
name="searchMode"
|
name="searchMode"
|
||||||
.options=${searchModeOptions}
|
.options=${searchModeOptions}
|
||||||
.value=${provider?.searchMode}
|
.value=${provider?.searchMode}
|
||||||
help=${msg("Configure how the outpost queries the core authentik server's users.")}
|
help=${msg(
|
||||||
|
"Configure how the outpost queries the core authentik server's users.",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
</ak-radio-input>
|
</ak-radio-input>
|
||||||
|
|
||||||
<ak-switch-input
|
<ak-switch-input
|
||||||
name="openInNewTab"
|
name="openInNewTab"
|
||||||
label=${msg("Code-based MFA Support")}
|
label=${msg("Code-based MFA Support")}
|
||||||
?checked=${provider?.mfaSupport}
|
?checked=${provider?.mfaSupport ?? true}
|
||||||
help=${mfaSupportHelp}
|
help=${mfaSupportHelp}
|
||||||
>
|
>
|
||||||
</ak-switch-input>
|
</ak-switch-input>
|
||||||
|
@ -100,13 +113,18 @@ export class ApplicationWizardApplicationDetails extends BaseProviderPanel {
|
||||||
label=${msg("Base DN")}
|
label=${msg("Base DN")}
|
||||||
required
|
required
|
||||||
value="${first(provider?.baseDn, "DC=ldap,DC=goauthentik,DC=io")}"
|
value="${first(provider?.baseDn, "DC=ldap,DC=goauthentik,DC=io")}"
|
||||||
|
.errorMessages=${errors?.baseDn ?? []}
|
||||||
help=${msg(
|
help=${msg(
|
||||||
"LDAP DN under which bind requests and search requests can be made.",
|
"LDAP DN under which bind requests and search requests can be made.",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
</ak-text-input>
|
</ak-text-input>
|
||||||
|
|
||||||
<ak-form-element-horizontal label=${msg("Certificate")} name="certificate">
|
<ak-form-element-horizontal
|
||||||
|
label=${msg("Certificate")}
|
||||||
|
name="certificate"
|
||||||
|
.errorMessages=${errors?.certificate ?? []}
|
||||||
|
>
|
||||||
<ak-crypto-certificate-search
|
<ak-crypto-certificate-search
|
||||||
certificate=${ifDefined(provider?.certificate ?? nothing)}
|
certificate=${ifDefined(provider?.certificate ?? nothing)}
|
||||||
name="certificate"
|
name="certificate"
|
||||||
|
@ -119,6 +137,7 @@ export class ApplicationWizardApplicationDetails extends BaseProviderPanel {
|
||||||
label=${msg("TLS Server name")}
|
label=${msg("TLS Server name")}
|
||||||
name="tlsServerName"
|
name="tlsServerName"
|
||||||
value="${first(provider?.tlsServerName, "")}"
|
value="${first(provider?.tlsServerName, "")}"
|
||||||
|
.errorMessages=${errors?.tlsServerName ?? []}
|
||||||
help=${tlsServerNameHelp}
|
help=${tlsServerNameHelp}
|
||||||
></ak-text-input>
|
></ak-text-input>
|
||||||
|
|
||||||
|
@ -127,6 +146,7 @@ export class ApplicationWizardApplicationDetails extends BaseProviderPanel {
|
||||||
required
|
required
|
||||||
name="uidStartNumber"
|
name="uidStartNumber"
|
||||||
value="${first(provider?.uidStartNumber, 2000)}"
|
value="${first(provider?.uidStartNumber, 2000)}"
|
||||||
|
.errorMessages=${errors?.uidStartNumber ?? []}
|
||||||
help=${uidStartNumberHelp}
|
help=${uidStartNumberHelp}
|
||||||
></ak-number-input>
|
></ak-number-input>
|
||||||
|
|
||||||
|
@ -135,6 +155,7 @@ export class ApplicationWizardApplicationDetails extends BaseProviderPanel {
|
||||||
required
|
required
|
||||||
name="gidStartNumber"
|
name="gidStartNumber"
|
||||||
value="${first(provider?.gidStartNumber, 4000)}"
|
value="${first(provider?.gidStartNumber, 4000)}"
|
||||||
|
.errorMessages=${errors?.gidStartNumber ?? []}
|
||||||
help=${gidStartNumberHelp}
|
help=${gidStartNumberHelp}
|
||||||
></ak-number-input>
|
></ak-number-input>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import "@goauthentik/admin/applications/wizard/ak-wizard-title";
|
||||||
import "@goauthentik/admin/common/ak-crypto-certificate-search";
|
import "@goauthentik/admin/common/ak-crypto-certificate-search";
|
||||||
import "@goauthentik/admin/common/ak-flow-search/ak-tenanted-flow-search";
|
import "@goauthentik/admin/common/ak-flow-search/ak-tenanted-flow-search";
|
||||||
import {
|
import {
|
||||||
|
@ -27,10 +28,10 @@ import {
|
||||||
PropertymappingsApi,
|
PropertymappingsApi,
|
||||||
SourcesApi,
|
SourcesApi,
|
||||||
} from "@goauthentik/api";
|
} from "@goauthentik/api";
|
||||||
import type {
|
import {
|
||||||
OAuth2Provider,
|
type OAuth2Provider,
|
||||||
PaginatedOAuthSourceList,
|
type PaginatedOAuthSourceList,
|
||||||
PaginatedScopeMappingList,
|
type PaginatedScopeMappingList,
|
||||||
} from "@goauthentik/api";
|
} from "@goauthentik/api";
|
||||||
|
|
||||||
import BaseProviderPanel from "../BaseProviderPanel";
|
import BaseProviderPanel from "../BaseProviderPanel";
|
||||||
|
@ -38,7 +39,7 @@ import BaseProviderPanel from "../BaseProviderPanel";
|
||||||
@customElement("ak-application-wizard-authentication-by-oauth")
|
@customElement("ak-application-wizard-authentication-by-oauth")
|
||||||
export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel {
|
export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel {
|
||||||
@state()
|
@state()
|
||||||
showClientSecret = false;
|
showClientSecret = true;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
propertyMappings?: PaginatedScopeMappingList;
|
propertyMappings?: PaginatedScopeMappingList;
|
||||||
|
@ -68,18 +69,22 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const provider = this.wizard.provider as OAuth2Provider | undefined;
|
const provider = this.wizard.provider as OAuth2Provider | undefined;
|
||||||
|
const errors = this.wizard.errors.provider;
|
||||||
|
|
||||||
return html`<form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
return html`<ak-wizard-title>${msg("Configure OAuth2/OpenId Provider")}</ak-wizard-title>
|
||||||
|
<form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
name="name"
|
name="name"
|
||||||
label=${msg("Name")}
|
label=${msg("Name")}
|
||||||
value=${ifDefined(provider?.name)}
|
value=${ifDefined(provider?.name)}
|
||||||
|
.errorMessages=${errors?.name ?? []}
|
||||||
required
|
required
|
||||||
></ak-text-input>
|
></ak-text-input>
|
||||||
|
|
||||||
<ak-form-element-horizontal
|
<ak-form-element-horizontal
|
||||||
name="authenticationFlow"
|
name="authenticationFlow"
|
||||||
label=${msg("Authentication flow")}
|
label=${msg("Authentication flow")}
|
||||||
|
.errorMessages=${errors?.authenticationFlow ?? []}
|
||||||
>
|
>
|
||||||
<ak-flow-search
|
<ak-flow-search
|
||||||
flowType=${FlowsInstancesListDesignationEnum.Authentication}
|
flowType=${FlowsInstancesListDesignationEnum.Authentication}
|
||||||
|
@ -87,13 +92,15 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel {
|
||||||
required
|
required
|
||||||
></ak-flow-search>
|
></ak-flow-search>
|
||||||
<p class="pf-c-form__helper-text">
|
<p class="pf-c-form__helper-text">
|
||||||
${msg("Flow used when a user access this provider and is not authenticated.")}
|
${msg(
|
||||||
|
"Flow used when a user access this provider and is not authenticated.",
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
|
||||||
<ak-form-element-horizontal
|
<ak-form-element-horizontal
|
||||||
name="authorizationFlow"
|
name="authorizationFlow"
|
||||||
label=${msg("Authorization flow")}
|
label=${msg("Authorization flow")}
|
||||||
|
.errorMessages=${errors?.authorizationFlow ?? []}
|
||||||
?required=${true}
|
?required=${true}
|
||||||
>
|
>
|
||||||
<ak-flow-search
|
<ak-flow-search
|
||||||
|
@ -124,10 +131,8 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel {
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
name="clientId"
|
name="clientId"
|
||||||
label=${msg("Client ID")}
|
label=${msg("Client ID")}
|
||||||
value="${first(
|
value=${provider?.clientId ?? randomString(40, ascii_letters + digits)}
|
||||||
provider?.clientId,
|
.errorMessages=${errors?.clientId ?? []}
|
||||||
randomString(40, ascii_letters + digits),
|
|
||||||
)}"
|
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
</ak-text-input>
|
</ak-text-input>
|
||||||
|
@ -135,10 +140,9 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel {
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
name="clientSecret"
|
name="clientSecret"
|
||||||
label=${msg("Client Secret")}
|
label=${msg("Client Secret")}
|
||||||
value="${first(
|
value=${provider?.clientSecret ??
|
||||||
provider?.clientSecret,
|
randomString(128, ascii_letters + digits)}
|
||||||
randomString(128, ascii_letters + digits),
|
.errorMessages=${errors?.clientSecret ?? []}
|
||||||
)}"
|
|
||||||
?hidden=${!this.showClientSecret}
|
?hidden=${!this.showClientSecret}
|
||||||
>
|
>
|
||||||
</ak-text-input>
|
</ak-text-input>
|
||||||
|
@ -147,18 +151,25 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel {
|
||||||
name="redirectUris"
|
name="redirectUris"
|
||||||
label=${msg("Redirect URIs/Origins (RegEx)")}
|
label=${msg("Redirect URIs/Origins (RegEx)")}
|
||||||
.value=${provider?.redirectUris}
|
.value=${provider?.redirectUris}
|
||||||
|
.errorMessages=${errors?.redirectUriHelp ?? []}
|
||||||
.bighelp=${redirectUriHelp}
|
.bighelp=${redirectUriHelp}
|
||||||
>
|
>
|
||||||
</ak-textarea-input>
|
</ak-textarea-input>
|
||||||
|
|
||||||
<ak-form-element-horizontal label=${msg("Signing Key")} name="signingKey">
|
<ak-form-element-horizontal
|
||||||
|
label=${msg("Signing Key")}
|
||||||
|
name="signingKey"
|
||||||
|
.errorMessages=${errors?.signingKey ?? []}
|
||||||
|
>
|
||||||
<ak-crypto-certificate-search
|
<ak-crypto-certificate-search
|
||||||
certificate=${ifDefined(provider?.signingKey ?? nothing)}
|
certificate=${ifDefined(provider?.signingKey ?? nothing)}
|
||||||
name="certificate"
|
name="certificate"
|
||||||
singleton
|
singleton
|
||||||
>
|
>
|
||||||
</ak-crypto-certificate-search>
|
</ak-crypto-certificate-search>
|
||||||
<p class="pf-c-form__helper-text">${msg("Key used to sign the tokens.")}</p>
|
<p class="pf-c-form__helper-text">
|
||||||
|
${msg("Key used to sign the tokens.")}
|
||||||
|
</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
</div>
|
</div>
|
||||||
</ak-form-group>
|
</ak-form-group>
|
||||||
|
@ -171,6 +182,7 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel {
|
||||||
label=${msg("Access code validity")}
|
label=${msg("Access code validity")}
|
||||||
required
|
required
|
||||||
value="${first(provider?.accessCodeValidity, "minutes=1")}"
|
value="${first(provider?.accessCodeValidity, "minutes=1")}"
|
||||||
|
.errorMessages=${errors?.accessCodeValidity ?? []}
|
||||||
.bighelp=${html`<p class="pf-c-form__helper-text">
|
.bighelp=${html`<p class="pf-c-form__helper-text">
|
||||||
${msg("Configure how long access codes are valid for.")}
|
${msg("Configure how long access codes are valid for.")}
|
||||||
</p>
|
</p>
|
||||||
|
@ -183,6 +195,7 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel {
|
||||||
label=${msg("Access Token validity")}
|
label=${msg("Access Token validity")}
|
||||||
value="${first(provider?.accessTokenValidity, "minutes=5")}"
|
value="${first(provider?.accessTokenValidity, "minutes=5")}"
|
||||||
required
|
required
|
||||||
|
.errorMessages=${errors?.accessTokenValidity ?? []}
|
||||||
.bighelp=${html` <p class="pf-c-form__helper-text">
|
.bighelp=${html` <p class="pf-c-form__helper-text">
|
||||||
${msg("Configure how long access tokens are valid for.")}
|
${msg("Configure how long access tokens are valid for.")}
|
||||||
</p>
|
</p>
|
||||||
|
@ -194,6 +207,7 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel {
|
||||||
name="refreshTokenValidity"
|
name="refreshTokenValidity"
|
||||||
label=${msg("Refresh Token validity")}
|
label=${msg("Refresh Token validity")}
|
||||||
value="${first(provider?.refreshTokenValidity, "days=30")}"
|
value="${first(provider?.refreshTokenValidity, "days=30")}"
|
||||||
|
.errorMessages=${errors?.refreshTokenValidity ?? []}
|
||||||
?required=${true}
|
?required=${true}
|
||||||
.bighelp=${html` <p class="pf-c-form__helper-text">
|
.bighelp=${html` <p class="pf-c-form__helper-text">
|
||||||
${msg("Configure how long refresh tokens are valid for.")}
|
${msg("Configure how long refresh tokens are valid for.")}
|
||||||
|
@ -202,7 +216,11 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel {
|
||||||
>
|
>
|
||||||
</ak-text-input>
|
</ak-text-input>
|
||||||
|
|
||||||
<ak-form-element-horizontal label=${msg("Scopes")} name="propertyMappings">
|
<ak-form-element-horizontal
|
||||||
|
label=${msg("Scopes")}
|
||||||
|
name="propertyMappings"
|
||||||
|
.errorMessages=${errors?.propertyMappings ?? []}
|
||||||
|
>
|
||||||
<select class="pf-c-form-control" multiple>
|
<select class="pf-c-form-control" multiple>
|
||||||
${this.propertyMappings?.results.map((scope) => {
|
${this.propertyMappings?.results.map((scope) => {
|
||||||
let selected = false;
|
let selected = false;
|
||||||
|
@ -212,9 +230,11 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel {
|
||||||
"goauthentik.io/providers/oauth2/scope-",
|
"goauthentik.io/providers/oauth2/scope-",
|
||||||
) || false;
|
) || false;
|
||||||
} else {
|
} else {
|
||||||
selected = Array.from(provider?.propertyMappings).some((su) => {
|
selected = Array.from(provider?.propertyMappings).some(
|
||||||
|
(su) => {
|
||||||
return su == scope.pk;
|
return su == scope.pk;
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return html`<option
|
return html`<option
|
||||||
value=${ifDefined(scope.pk)}
|
value=${ifDefined(scope.pk)}
|
||||||
|
@ -273,6 +293,7 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel {
|
||||||
<ak-form-element-horizontal
|
<ak-form-element-horizontal
|
||||||
label=${msg("Trusted OIDC Sources")}
|
label=${msg("Trusted OIDC Sources")}
|
||||||
name="jwksSources"
|
name="jwksSources"
|
||||||
|
.errorMessages=${errors?.jwksSources ?? []}
|
||||||
>
|
>
|
||||||
<select class="pf-c-form-control" multiple>
|
<select class="pf-c-form-control" multiple>
|
||||||
${this.oauthSources?.results.map((source) => {
|
${this.oauthSources?.results.map((source) => {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import "@goauthentik/admin/applications/wizard/ak-wizard-title";
|
||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import { first } from "@goauthentik/common/utils";
|
import { first } from "@goauthentik/common/utils";
|
||||||
import "@goauthentik/components/ak-switch-input";
|
import "@goauthentik/components/ak-switch-input";
|
||||||
|
@ -61,11 +62,11 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel {
|
||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderProxyMode() {
|
renderProxyMode(): TemplateResult {
|
||||||
return html`<h2>This space intentionally left blank</h2>`;
|
throw new Error("Must be implemented in a child class.");
|
||||||
}
|
}
|
||||||
|
|
||||||
renderHttpBasic(): TemplateResult {
|
renderHttpBasic() {
|
||||||
return html`<ak-text-input
|
return html`<ak-text-input
|
||||||
name="basicAuthUserAttribute"
|
name="basicAuthUserAttribute"
|
||||||
label=${msg("HTTP-Basic Username Key")}
|
label=${msg("HTTP-Basic Username Key")}
|
||||||
|
@ -87,19 +88,45 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel {
|
||||||
</ak-text-input>`;
|
</ak-text-input>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scopeMappingConfiguration(provider?: ProxyProvider) {
|
||||||
|
const propertyMappings = this.propertyMappings?.results ?? [];
|
||||||
|
|
||||||
|
const defaultScopes = () =>
|
||||||
|
propertyMappings
|
||||||
|
.filter((scope) => !(scope?.managed ?? "").startsWith("goauthentik.io/providers"))
|
||||||
|
.map((pm) => pm.pk);
|
||||||
|
|
||||||
|
const configuredScopes = (providerMappings: string[]) =>
|
||||||
|
propertyMappings.map((scope) => scope.pk).filter((pk) => providerMappings.includes(pk));
|
||||||
|
|
||||||
|
const scopeValues = provider?.propertyMappings
|
||||||
|
? configuredScopes(provider?.propertyMappings ?? [])
|
||||||
|
: defaultScopes();
|
||||||
|
|
||||||
|
const scopePairs = propertyMappings.map((scope) => [scope.pk, scope.name]);
|
||||||
|
|
||||||
|
return { scopePairs, scopeValues };
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`<form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
const errors = this.wizard.errors.provider;
|
||||||
|
const { scopePairs, scopeValues } = this.scopeMappingConfiguration(this.instance);
|
||||||
|
|
||||||
|
return html` <ak-wizard-title>${msg("Configure Proxy Provider")}</ak-wizard-title>
|
||||||
|
<form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
||||||
${this.renderModeDescription()}
|
${this.renderModeDescription()}
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
name="name"
|
name="name"
|
||||||
value=${ifDefined(this.instance?.name)}
|
value=${ifDefined(this.instance?.name)}
|
||||||
required
|
required
|
||||||
|
.errorMessages=${errors?.name ?? []}
|
||||||
label=${msg("Name")}
|
label=${msg("Name")}
|
||||||
></ak-text-input>
|
></ak-text-input>
|
||||||
|
|
||||||
<ak-form-element-horizontal
|
<ak-form-element-horizontal
|
||||||
label=${msg("Authentication flow")}
|
label=${msg("Authentication flow")}
|
||||||
?required=${false}
|
?required=${false}
|
||||||
|
.errorMessages=${errors?.authenticationFlow ?? []}
|
||||||
name="authenticationFlow"
|
name="authenticationFlow"
|
||||||
>
|
>
|
||||||
<ak-flow-search
|
<ak-flow-search
|
||||||
|
@ -108,7 +135,9 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel {
|
||||||
required
|
required
|
||||||
></ak-flow-search>
|
></ak-flow-search>
|
||||||
<p class="pf-c-form__helper-text">
|
<p class="pf-c-form__helper-text">
|
||||||
${msg("Flow used when a user access this provider and is not authenticated.")}
|
${msg(
|
||||||
|
"Flow used when a user access this provider and is not authenticated.",
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
|
||||||
|
@ -116,6 +145,7 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel {
|
||||||
label=${msg("Authorization flow")}
|
label=${msg("Authorization flow")}
|
||||||
?required=${true}
|
?required=${true}
|
||||||
name="authorizationFlow"
|
name="authorizationFlow"
|
||||||
|
.errorMessages=${errors?.authorizationFlow ?? []}
|
||||||
>
|
>
|
||||||
<ak-flow-search
|
<ak-flow-search
|
||||||
flowType=${FlowsInstancesListDesignationEnum.Authorization}
|
flowType=${FlowsInstancesListDesignationEnum.Authorization}
|
||||||
|
@ -134,47 +164,40 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel {
|
||||||
value=${first(this.instance?.accessTokenValidity, "hours=24")}
|
value=${first(this.instance?.accessTokenValidity, "hours=24")}
|
||||||
label=${msg("Token validity")}
|
label=${msg("Token validity")}
|
||||||
help=${msg("Configure how long tokens are valid for.")}
|
help=${msg("Configure how long tokens are valid for.")}
|
||||||
|
.errorMessages=${errors?.accessTokenValidity ?? []}
|
||||||
></ak-text-input>
|
></ak-text-input>
|
||||||
|
|
||||||
<ak-form-group>
|
<ak-form-group>
|
||||||
<span slot="header">${msg("Advanced protocol settings")}</span>
|
<span slot="header">${msg("Advanced protocol settings")}</span>
|
||||||
<div slot="body" class="pf-c-form">
|
<div slot="body" class="pf-c-form">
|
||||||
<ak-form-element-horizontal label=${msg("Certificate")} name="certificate">
|
<ak-form-element-horizontal
|
||||||
|
label=${msg("Certificate")}
|
||||||
|
name="certificate"
|
||||||
|
.errorMessages=${errors?.certificate ?? []}
|
||||||
|
>
|
||||||
<ak-crypto-certificate-search
|
<ak-crypto-certificate-search
|
||||||
certificate=${ifDefined(this.instance?.certificate ?? undefined)}
|
certificate=${ifDefined(this.instance?.certificate ?? undefined)}
|
||||||
></ak-crypto-certificate-search>
|
></ak-crypto-certificate-search>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
|
||||||
<ak-form-element-horizontal
|
<ak-multi-select
|
||||||
label=${msg("Additional scopes")}
|
label=${msg("AdditionalScopes")}
|
||||||
name="propertyMappings"
|
name="propertyMappings"
|
||||||
>
|
required
|
||||||
<select class="pf-c-form-control" multiple>
|
.options=${scopePairs}
|
||||||
${this.propertyMappings?.results
|
.values=${scopeValues}
|
||||||
.filter((scope) => {
|
.errorMessages=${errors?.propertyMappings ?? []}
|
||||||
return !scope.managed?.startsWith("goauthentik.io/providers");
|
.richhelp=${html`
|
||||||
})
|
|
||||||
.map((scope) => {
|
|
||||||
const selected = (this.instance?.propertyMappings || []).some(
|
|
||||||
(su) => {
|
|
||||||
return su == scope.pk;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return html`<option
|
|
||||||
value=${ifDefined(scope.pk)}
|
|
||||||
?selected=${selected}
|
|
||||||
>
|
|
||||||
${scope.name}
|
|
||||||
</option>`;
|
|
||||||
})}
|
|
||||||
</select>
|
|
||||||
<p class="pf-c-form__helper-text">
|
<p class="pf-c-form__helper-text">
|
||||||
${msg("Additional scope mappings, which are passed to the proxy.")}
|
${msg(
|
||||||
|
"Additional scope mappings, which are passed to the proxy.",
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<p class="pf-c-form__helper-text">
|
<p class="pf-c-form__helper-text">
|
||||||
${msg("Hold control/command to select multiple items.")}
|
${msg("Hold control/command to select multiple items.")}
|
||||||
</p>
|
</p>
|
||||||
</ak-form-element-horizontal>
|
`}
|
||||||
|
></ak-multi-select>
|
||||||
|
|
||||||
<ak-textarea-input
|
<ak-textarea-input
|
||||||
name="skipPathRegex"
|
name="skipPathRegex"
|
||||||
|
@ -182,6 +205,7 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel {
|
||||||
? msg("Unauthenticated URLs")
|
? msg("Unauthenticated URLs")
|
||||||
: msg("Unauthenticated Paths")}
|
: msg("Unauthenticated Paths")}
|
||||||
value=${ifDefined(this.instance?.skipPathRegex)}
|
value=${ifDefined(this.instance?.skipPathRegex)}
|
||||||
|
.errorMessages=${errors?.skipPathRegex ?? []}
|
||||||
.bighelp=${html` <p class="pf-c-form__helper-text">
|
.bighelp=${html` <p class="pf-c-form__helper-text">
|
||||||
${msg(
|
${msg(
|
||||||
"Regular expressions for which authentication is not required. Each new line is interpreted as a new expression.",
|
"Regular expressions for which authentication is not required. Each new line is interpreted as a new expression.",
|
||||||
|
@ -226,12 +250,15 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel {
|
||||||
<ak-form-element-horizontal
|
<ak-form-element-horizontal
|
||||||
label=${msg("Trusted OIDC Sources")}
|
label=${msg("Trusted OIDC Sources")}
|
||||||
name="jwksSources"
|
name="jwksSources"
|
||||||
|
.errorMessages=${errors?.jwksSources ?? []}
|
||||||
>
|
>
|
||||||
<select class="pf-c-form-control" multiple>
|
<select class="pf-c-form-control" multiple>
|
||||||
${this.oauthSources?.results.map((source) => {
|
${this.oauthSources?.results.map((source) => {
|
||||||
const selected = (this.instance?.jwksSources || []).some((su) => {
|
const selected = (this.instance?.jwksSources || []).some(
|
||||||
|
(su) => {
|
||||||
return su == source.pk;
|
return su == source.pk;
|
||||||
});
|
},
|
||||||
|
);
|
||||||
return html`<option value=${source.pk} ?selected=${selected}>
|
return html`<option value=${source.pk} ?selected=${selected}>
|
||||||
${source.name} (${source.slug})
|
${source.name} (${source.slug})
|
||||||
</option>`;
|
</option>`;
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { customElement } from "@lit/reactive-element/decorators.js";
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
|
import { ProxyProvider } from "@goauthentik/api";
|
||||||
|
|
||||||
import AkTypeProxyApplicationWizardPage from "./AuthenticationByProxyPage";
|
import AkTypeProxyApplicationWizardPage from "./AuthenticationByProxyPage";
|
||||||
|
|
||||||
@customElement("ak-application-wizard-authentication-for-forward-proxy-domain")
|
@customElement("ak-application-wizard-authentication-for-forward-proxy-domain")
|
||||||
|
@ -28,11 +30,15 @@ export class AkForwardDomainProxyApplicationWizardPage extends AkTypeProxyApplic
|
||||||
}
|
}
|
||||||
|
|
||||||
renderProxyMode() {
|
renderProxyMode() {
|
||||||
|
const provider = this.wizard.provider as ProxyProvider | undefined;
|
||||||
|
const errors = this.wizard.errors.provider;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
name="externalHost"
|
name="externalHost"
|
||||||
label=${msg("External host")}
|
label=${msg("External host")}
|
||||||
value=${ifDefined(this.instance?.externalHost)}
|
value=${ifDefined(provider?.externalHost)}
|
||||||
|
.errorMessages=${errors?.externalHost ?? []}
|
||||||
required
|
required
|
||||||
help=${msg(
|
help=${msg(
|
||||||
"The external URL you'll authenticate at. The authentik core server should be reachable under this URL.",
|
"The external URL you'll authenticate at. The authentik core server should be reachable under this URL.",
|
||||||
|
@ -42,7 +48,8 @@ export class AkForwardDomainProxyApplicationWizardPage extends AkTypeProxyApplic
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
name="cookieDomain"
|
name="cookieDomain"
|
||||||
label=${msg("Cookie domain")}
|
label=${msg("Cookie domain")}
|
||||||
value="${ifDefined(this.instance?.cookieDomain)}"
|
value="${ifDefined(provider?.cookieDomain)}"
|
||||||
|
.errorMessages=${errors?.cookieDomain ?? []}
|
||||||
required
|
required
|
||||||
help=${msg(
|
help=${msg(
|
||||||
"Set this to the domain you wish the authentication to be valid for. Must be a parent domain of the URL above. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'.",
|
"Set this to the domain you wish the authentication to be valid for. Must be a parent domain of the URL above. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'.",
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { customElement } from "@lit/reactive-element/decorators.js";
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
|
import { ProxyProvider } from "@goauthentik/api";
|
||||||
|
|
||||||
import AkTypeProxyApplicationWizardPage from "./AuthenticationByProxyPage";
|
import AkTypeProxyApplicationWizardPage from "./AuthenticationByProxyPage";
|
||||||
|
|
||||||
@customElement("ak-application-wizard-authentication-for-reverse-proxy")
|
@customElement("ak-application-wizard-authentication-for-reverse-proxy")
|
||||||
|
@ -20,25 +22,30 @@ export class AkReverseProxyApplicationWizardPage extends AkTypeProxyApplicationW
|
||||||
}
|
}
|
||||||
|
|
||||||
renderProxyMode() {
|
renderProxyMode() {
|
||||||
|
const provider = this.wizard.provider as ProxyProvider | undefined;
|
||||||
|
const errors = this.wizard.errors.provider;
|
||||||
|
|
||||||
return html` <ak-text-input
|
return html` <ak-text-input
|
||||||
name="externalHost"
|
name="externalHost"
|
||||||
value=${ifDefined(this.instance?.externalHost)}
|
value=${ifDefined(provider?.externalHost)}
|
||||||
required
|
required
|
||||||
label=${msg("External host")}
|
label=${msg("External host")}
|
||||||
|
.errorMessages=${errors?.externalHost ?? []}
|
||||||
help=${msg(
|
help=${msg(
|
||||||
"The external URL you'll access the application at. Include any non-standard port.",
|
"The external URL you'll access the application at. Include any non-standard port.",
|
||||||
)}
|
)}
|
||||||
></ak-text-input>
|
></ak-text-input>
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
name="internalHost"
|
name="internalHost"
|
||||||
value=${ifDefined(this.instance?.internalHost)}
|
value=${ifDefined(provider?.internalHost)}
|
||||||
|
.errorMessages=${errors?.internalHost ?? []}
|
||||||
required
|
required
|
||||||
label=${msg("Internal host")}
|
label=${msg("Internal host")}
|
||||||
help=${msg("Upstream host that the requests are forwarded to.")}
|
help=${msg("Upstream host that the requests are forwarded to.")}
|
||||||
></ak-text-input>
|
></ak-text-input>
|
||||||
<ak-switch-input
|
<ak-switch-input
|
||||||
name="internalHostSslValidation"
|
name="internalHostSslValidation"
|
||||||
?checked=${first(this.instance?.internalHostSslValidation, true)}
|
?checked=${first(provider?.internalHostSslValidation, true)}
|
||||||
label=${msg("Internal host SSL Validation")}
|
label=${msg("Internal host SSL Validation")}
|
||||||
help=${msg("Validate SSL Certificates of upstream servers.")}
|
help=${msg("Validate SSL Certificates of upstream servers.")}
|
||||||
>
|
>
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { customElement } from "@lit/reactive-element/decorators.js";
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
|
import { ProxyProvider } from "@goauthentik/api";
|
||||||
|
|
||||||
import AkTypeProxyApplicationWizardPage from "./AuthenticationByProxyPage";
|
import AkTypeProxyApplicationWizardPage from "./AuthenticationByProxyPage";
|
||||||
|
|
||||||
@customElement("ak-application-wizard-authentication-for-single-forward-proxy")
|
@customElement("ak-application-wizard-authentication-for-single-forward-proxy")
|
||||||
|
@ -21,11 +23,15 @@ export class AkForwardSingleProxyApplicationWizardPage extends AkTypeProxyApplic
|
||||||
}
|
}
|
||||||
|
|
||||||
renderProxyMode() {
|
renderProxyMode() {
|
||||||
|
const provider = this.wizard.provider as ProxyProvider | undefined;
|
||||||
|
const errors = this.wizard.errors.provider;
|
||||||
|
|
||||||
return html`<ak-text-input
|
return html`<ak-text-input
|
||||||
name="externalHost"
|
name="externalHost"
|
||||||
value=${ifDefined(this.instance?.externalHost)}
|
value=${ifDefined(provider?.externalHost)}
|
||||||
required
|
required
|
||||||
label=${msg("External host")}
|
label=${msg("External host")}
|
||||||
|
.errorMessages=${errors?.externalHost ?? []}
|
||||||
help=${msg(
|
help=${msg(
|
||||||
"The external URL you'll access the application at. Include any non-standard port.",
|
"The external URL you'll access the application at. Include any non-standard port.",
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import "@goauthentik/admin/applications/wizard/ak-wizard-title";
|
||||||
import "@goauthentik/admin/common/ak-crypto-certificate-search";
|
import "@goauthentik/admin/common/ak-crypto-certificate-search";
|
||||||
import "@goauthentik/admin/common/ak-flow-search/ak-tenanted-flow-search";
|
import "@goauthentik/admin/common/ak-flow-search/ak-tenanted-flow-search";
|
||||||
import { ascii_letters, digits, first, randomString } from "@goauthentik/common/utils";
|
import { ascii_letters, digits, first, randomString } from "@goauthentik/common/utils";
|
||||||
|
@ -19,12 +20,15 @@ import BaseProviderPanel from "../BaseProviderPanel";
|
||||||
export class ApplicationWizardAuthenticationByRadius extends BaseProviderPanel {
|
export class ApplicationWizardAuthenticationByRadius extends BaseProviderPanel {
|
||||||
render() {
|
render() {
|
||||||
const provider = this.wizard.provider as RadiusProvider | undefined;
|
const provider = this.wizard.provider as RadiusProvider | undefined;
|
||||||
|
const errors = this.wizard.errors.provider;
|
||||||
|
|
||||||
return html`<form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
return html`<ak-wizard-title>${msg("Configure Radius Provider")}</ak-wizard-title>
|
||||||
|
<form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
name="name"
|
name="name"
|
||||||
label=${msg("Name")}
|
label=${msg("Name")}
|
||||||
value=${ifDefined(provider?.name)}
|
value=${ifDefined(provider?.name)}
|
||||||
|
.errorMessages=${errors?.name ?? []}
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
</ak-text-input>
|
</ak-text-input>
|
||||||
|
@ -33,6 +37,7 @@ export class ApplicationWizardAuthenticationByRadius extends BaseProviderPanel {
|
||||||
label=${msg("Authentication flow")}
|
label=${msg("Authentication flow")}
|
||||||
?required=${true}
|
?required=${true}
|
||||||
name="authorizationFlow"
|
name="authorizationFlow"
|
||||||
|
.errorMessages=${errors?.authorizationFlow ?? []}
|
||||||
>
|
>
|
||||||
<ak-tenanted-flow-search
|
<ak-tenanted-flow-search
|
||||||
flowType=${FlowsInstancesListDesignationEnum.Authentication}
|
flowType=${FlowsInstancesListDesignationEnum.Authentication}
|
||||||
|
@ -40,7 +45,9 @@ export class ApplicationWizardAuthenticationByRadius extends BaseProviderPanel {
|
||||||
.tenantFlow=${rootInterface()?.tenant?.flowAuthentication}
|
.tenantFlow=${rootInterface()?.tenant?.flowAuthentication}
|
||||||
required
|
required
|
||||||
></ak-tenanted-flow-search>
|
></ak-tenanted-flow-search>
|
||||||
<p class="pf-c-form__helper-text">${msg("Flow used for users to authenticate.")}</p>
|
<p class="pf-c-form__helper-text">
|
||||||
|
${msg("Flow used for users to authenticate.")}
|
||||||
|
</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
|
||||||
<ak-form-group expanded>
|
<ak-form-group expanded>
|
||||||
|
@ -49,6 +56,7 @@ export class ApplicationWizardAuthenticationByRadius extends BaseProviderPanel {
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
name="sharedSecret"
|
name="sharedSecret"
|
||||||
label=${msg("Shared secret")}
|
label=${msg("Shared secret")}
|
||||||
|
.errorMessages=${errors?.sharedSecret ?? []}
|
||||||
value=${first(
|
value=${first(
|
||||||
provider?.sharedSecret,
|
provider?.sharedSecret,
|
||||||
randomString(128, ascii_letters + digits),
|
randomString(128, ascii_letters + digits),
|
||||||
|
@ -59,6 +67,7 @@ export class ApplicationWizardAuthenticationByRadius extends BaseProviderPanel {
|
||||||
name="clientNetworks"
|
name="clientNetworks"
|
||||||
label=${msg("Client Networks")}
|
label=${msg("Client Networks")}
|
||||||
value=${first(provider?.clientNetworks, "0.0.0.0/0, ::/0")}
|
value=${first(provider?.clientNetworks, "0.0.0.0/0, ::/0")}
|
||||||
|
.errorMessages=${errors?.clientNetworks ?? []}
|
||||||
required
|
required
|
||||||
help=${msg(`List of CIDRs (comma-seperated) that clients can connect from. A more specific
|
help=${msg(`List of CIDRs (comma-seperated) that clients can connect from. A more specific
|
||||||
CIDR will match before a looser one. Clients connecting from a non-specified CIDR
|
CIDR will match before a looser one. Clients connecting from a non-specified CIDR
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
import "@goauthentik/admin/applications/wizard/ak-wizard-title";
|
||||||
|
import "@goauthentik/admin/applications/wizard/ak-wizard-title";
|
||||||
import "@goauthentik/admin/common/ak-core-group-search";
|
import "@goauthentik/admin/common/ak-core-group-search";
|
||||||
import "@goauthentik/admin/common/ak-crypto-certificate-search";
|
import "@goauthentik/admin/common/ak-crypto-certificate-search";
|
||||||
import "@goauthentik/admin/common/ak-flow-search/ak-tenanted-flow-search";
|
import "@goauthentik/admin/common/ak-flow-search/ak-tenanted-flow-search";
|
||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
|
import "@goauthentik/components/ak-multi-select";
|
||||||
import "@goauthentik/components/ak-number-input";
|
import "@goauthentik/components/ak-number-input";
|
||||||
import "@goauthentik/components/ak-radio-input";
|
import "@goauthentik/components/ak-radio-input";
|
||||||
import "@goauthentik/components/ak-switch-input";
|
import "@goauthentik/components/ak-switch-input";
|
||||||
|
@ -10,7 +13,7 @@ import "@goauthentik/elements/forms/FormGroup";
|
||||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||||
|
|
||||||
import { msg } from "@lit/localize";
|
import { msg } from "@lit/localize";
|
||||||
import { customElement } from "@lit/reactive-element/decorators/custom-element.js";
|
import { customElement, state } from "@lit/reactive-element/decorators.js";
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
|
@ -27,9 +30,11 @@ import {
|
||||||
signatureAlgorithmOptions,
|
signatureAlgorithmOptions,
|
||||||
spBindingOptions,
|
spBindingOptions,
|
||||||
} from "./SamlProviderOptions";
|
} from "./SamlProviderOptions";
|
||||||
|
import "./saml-property-mappings-search";
|
||||||
|
|
||||||
@customElement("ak-application-wizard-authentication-by-saml-configuration")
|
@customElement("ak-application-wizard-authentication-by-saml-configuration")
|
||||||
export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPanel {
|
export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPanel {
|
||||||
|
@state()
|
||||||
propertyMappings?: PaginatedSAMLPropertyMappingList;
|
propertyMappings?: PaginatedSAMLPropertyMappingList;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -43,21 +48,47 @@ export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPane
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
propertyMappingConfiguration(provider?: SAMLProvider) {
|
||||||
|
const propertyMappings = this.propertyMappings?.results ?? [];
|
||||||
|
|
||||||
|
const configuredMappings = (providerMappings: string[]) =>
|
||||||
|
propertyMappings.map((pm) => pm.pk).filter((pmpk) => providerMappings.includes(pmpk));
|
||||||
|
|
||||||
|
const managedMappings = () =>
|
||||||
|
propertyMappings
|
||||||
|
.filter((pm) => (pm?.managed ?? "").startsWith("goauthentik.io/providers/saml"))
|
||||||
|
.map((pm) => pm.pk);
|
||||||
|
|
||||||
|
const pmValues = provider?.propertyMappings
|
||||||
|
? configuredMappings(provider?.propertyMappings ?? [])
|
||||||
|
: managedMappings();
|
||||||
|
|
||||||
|
const propertyPairs = propertyMappings.map((pm) => [pm.pk, pm.name]);
|
||||||
|
|
||||||
|
return { pmValues, propertyPairs };
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const provider = this.wizard.provider as SAMLProvider | undefined;
|
const provider = this.wizard.provider as SAMLProvider | undefined;
|
||||||
|
const errors = this.wizard.errors.provider;
|
||||||
|
|
||||||
return html` <form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
const { pmValues, propertyPairs } = this.propertyMappingConfiguration(provider);
|
||||||
|
|
||||||
|
return html` <ak-wizard-title>${msg("Configure SAML Provider")}</ak-wizard-title>
|
||||||
|
<form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
name="name"
|
name="name"
|
||||||
value=${ifDefined(provider?.name)}
|
value=${ifDefined(provider?.name)}
|
||||||
required
|
required
|
||||||
label=${msg("Name")}
|
label=${msg("Name")}
|
||||||
|
.errorMessages=${errors?.name ?? []}
|
||||||
></ak-text-input>
|
></ak-text-input>
|
||||||
|
|
||||||
<ak-form-element-horizontal
|
<ak-form-element-horizontal
|
||||||
label=${msg("Authentication flow")}
|
label=${msg("Authentication flow")}
|
||||||
?required=${false}
|
?required=${false}
|
||||||
name="authenticationFlow"
|
name="authenticationFlow"
|
||||||
|
.errorMessages=${errors?.authenticationFlow ?? []}
|
||||||
>
|
>
|
||||||
<ak-flow-search
|
<ak-flow-search
|
||||||
flowType=${FlowsInstancesListDesignationEnum.Authentication}
|
flowType=${FlowsInstancesListDesignationEnum.Authentication}
|
||||||
|
@ -65,7 +96,9 @@ export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPane
|
||||||
required
|
required
|
||||||
></ak-flow-search>
|
></ak-flow-search>
|
||||||
<p class="pf-c-form__helper-text">
|
<p class="pf-c-form__helper-text">
|
||||||
${msg("Flow used when a user access this provider and is not authenticated.")}
|
${msg(
|
||||||
|
"Flow used when a user access this provider and is not authenticated.",
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
|
||||||
|
@ -73,6 +106,7 @@ export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPane
|
||||||
label=${msg("Authorization flow")}
|
label=${msg("Authorization flow")}
|
||||||
?required=${true}
|
?required=${true}
|
||||||
name="authorizationFlow"
|
name="authorizationFlow"
|
||||||
|
.errorMessages=${errors?.authorizationFlow ?? []}
|
||||||
>
|
>
|
||||||
<ak-flow-search
|
<ak-flow-search
|
||||||
flowType=${FlowsInstancesListDesignationEnum.Authorization}
|
flowType=${FlowsInstancesListDesignationEnum.Authorization}
|
||||||
|
@ -92,6 +126,7 @@ export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPane
|
||||||
value=${ifDefined(provider?.acsUrl)}
|
value=${ifDefined(provider?.acsUrl)}
|
||||||
required
|
required
|
||||||
label=${msg("ACS URL")}
|
label=${msg("ACS URL")}
|
||||||
|
.errorMessages=${errors?.acsUrl ?? []}
|
||||||
></ak-text-input>
|
></ak-text-input>
|
||||||
|
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
|
@ -100,6 +135,7 @@ export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPane
|
||||||
required
|
required
|
||||||
label=${msg("Issuer")}
|
label=${msg("Issuer")}
|
||||||
help=${msg("Also known as EntityID.")}
|
help=${msg("Also known as EntityID.")}
|
||||||
|
.errorMessages=${errors?.issuer ?? []}
|
||||||
></ak-text-input>
|
></ak-text-input>
|
||||||
|
|
||||||
<ak-radio-input
|
<ak-radio-input
|
||||||
|
@ -118,6 +154,7 @@ export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPane
|
||||||
name="audience"
|
name="audience"
|
||||||
value=${ifDefined(provider?.audience)}
|
value=${ifDefined(provider?.audience)}
|
||||||
label=${msg("Audience")}
|
label=${msg("Audience")}
|
||||||
|
.errorMessages=${errors?.audience ?? []}
|
||||||
></ak-text-input>
|
></ak-text-input>
|
||||||
</div>
|
</div>
|
||||||
</ak-form-group>
|
</ak-form-group>
|
||||||
|
@ -154,36 +191,19 @@ export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPane
|
||||||
</p>
|
</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
|
||||||
<ak-form-element-horizontal
|
<ak-multi-select
|
||||||
label=${msg("Property mappings")}
|
label=${msg("Property Mappings")}
|
||||||
?required=${true}
|
|
||||||
name="propertyMappings"
|
name="propertyMappings"
|
||||||
>
|
required
|
||||||
<select class="pf-c-form-control" multiple>
|
.options=${propertyPairs}
|
||||||
${this.propertyMappings?.results.map((mapping) => {
|
.values=${pmValues}
|
||||||
let selected = false;
|
.richhelp=${html` <p class="pf-c-form__helper-text">
|
||||||
if (!provider?.propertyMappings) {
|
${msg("Property mappings used for user mapping.")}
|
||||||
selected =
|
</p>
|
||||||
mapping.managed?.startsWith(
|
|
||||||
"goauthentik.io/providers/saml",
|
|
||||||
) || false;
|
|
||||||
} else {
|
|
||||||
selected = Array.from(provider?.propertyMappings).some((su) => {
|
|
||||||
return su == mapping.pk;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return html`<option
|
|
||||||
value=${ifDefined(mapping.pk)}
|
|
||||||
?selected=${selected}
|
|
||||||
>
|
|
||||||
${mapping.name}
|
|
||||||
</option>`;
|
|
||||||
})}
|
|
||||||
</select>
|
|
||||||
<p class="pf-c-form__helper-text">
|
<p class="pf-c-form__helper-text">
|
||||||
${msg("Hold control/command to select multiple items.")}
|
${msg("Hold control/command to select multiple items.")}
|
||||||
</p>
|
</p>`}
|
||||||
</ak-form-element-horizontal>
|
></ak-multi-select>
|
||||||
|
|
||||||
<ak-form-element-horizontal
|
<ak-form-element-horizontal
|
||||||
label=${msg("NameID Property Mapping")}
|
label=${msg("NameID Property Mapping")}
|
||||||
|
@ -205,7 +225,10 @@ export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPane
|
||||||
value=${provider?.assertionValidNotBefore || "minutes=-5"}
|
value=${provider?.assertionValidNotBefore || "minutes=-5"}
|
||||||
required
|
required
|
||||||
label=${msg("Assertion valid not before")}
|
label=${msg("Assertion valid not before")}
|
||||||
help=${msg("Configure the maximum allowed time drift for an assertion.")}
|
help=${msg(
|
||||||
|
"Configure the maximum allowed time drift for an assertion.",
|
||||||
|
)}
|
||||||
|
.errorMessages=${errors?.assertionValidNotBefore ?? []}
|
||||||
></ak-text-input>
|
></ak-text-input>
|
||||||
|
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
|
@ -213,7 +236,10 @@ export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPane
|
||||||
value=${provider?.assertionValidNotOnOrAfter || "minutes=5"}
|
value=${provider?.assertionValidNotOnOrAfter || "minutes=5"}
|
||||||
required
|
required
|
||||||
label=${msg("Assertion valid not on or after")}
|
label=${msg("Assertion valid not on or after")}
|
||||||
help=${msg("Assertion not valid on or after current time + this value.")}
|
help=${msg(
|
||||||
|
"Assertion not valid on or after current time + this value.",
|
||||||
|
)}
|
||||||
|
.errorMessages=${errors?.assertionValidNotOnOrAfter ?? []}
|
||||||
></ak-text-input>
|
></ak-text-input>
|
||||||
|
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
|
@ -222,6 +248,7 @@ export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPane
|
||||||
required
|
required
|
||||||
label=${msg("Session valid not on or after")}
|
label=${msg("Session valid not on or after")}
|
||||||
help=${msg("Session not valid on or after current time + this value.")}
|
help=${msg("Session not valid on or after current time + this value.")}
|
||||||
|
.errorMessages=${errors?.sessionValidNotOnOrAfter ?? []}
|
||||||
></ak-text-input>
|
></ak-text-input>
|
||||||
|
|
||||||
<ak-radio-input
|
<ak-radio-input
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
import "@goauthentik/admin/common/ak-flow-search/ak-flow-search-no-default";
|
|
||||||
import "@goauthentik/components/ak-file-input";
|
|
||||||
import { AkFileInput } from "@goauthentik/components/ak-file-input";
|
|
||||||
import "@goauthentik/components/ak-text-input";
|
|
||||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
|
||||||
|
|
||||||
import { msg } from "@lit/localize";
|
|
||||||
import { customElement } from "@lit/reactive-element/decorators/custom-element.js";
|
|
||||||
import { html } from "lit";
|
|
||||||
import { query } from "lit/decorators.js";
|
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
|
||||||
|
|
||||||
import {
|
|
||||||
FlowsInstancesListDesignationEnum,
|
|
||||||
ProvidersSamlImportMetadataCreateRequest,
|
|
||||||
} from "@goauthentik/api";
|
|
||||||
|
|
||||||
import BaseProviderPanel from "../BaseProviderPanel";
|
|
||||||
|
|
||||||
@customElement("ak-application-wizard-authentication-by-saml-import")
|
|
||||||
export class ApplicationWizardProviderSamlImport extends BaseProviderPanel {
|
|
||||||
@query('ak-file-input[name="metadata"]')
|
|
||||||
fileInput!: AkFileInput;
|
|
||||||
|
|
||||||
handleChange(ev: InputEvent) {
|
|
||||||
if (!ev.target) {
|
|
||||||
console.warn(`Received event with no target: ${ev}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const target = ev.target as HTMLInputElement;
|
|
||||||
if (target.type === "file") {
|
|
||||||
const file = this.fileInput.files?.[0] ?? null;
|
|
||||||
if (file) {
|
|
||||||
this.dispatchWizardUpdate({
|
|
||||||
update: {
|
|
||||||
provider: {
|
|
||||||
file,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
status: this.form.checkValidity() ? "valid" : "invalid",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
super.handleChange(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const provider = this.wizard.provider as
|
|
||||||
| ProvidersSamlImportMetadataCreateRequest
|
|
||||||
| undefined;
|
|
||||||
|
|
||||||
return html` <form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
|
||||||
<ak-text-input
|
|
||||||
name="name"
|
|
||||||
value=${ifDefined(provider?.name)}
|
|
||||||
label=${msg("Name")}
|
|
||||||
required
|
|
||||||
help=${msg("Method's display Name.")}
|
|
||||||
></ak-text-input>
|
|
||||||
|
|
||||||
<ak-form-element-horizontal
|
|
||||||
label=${msg("Authorization flow")}
|
|
||||||
?required=${true}
|
|
||||||
name="authorizationFlow"
|
|
||||||
>
|
|
||||||
<ak-flow-search-no-default
|
|
||||||
flowType=${FlowsInstancesListDesignationEnum.Authorization}
|
|
||||||
required
|
|
||||||
></ak-flow-search-no-default>
|
|
||||||
<p class="pf-c-form__helper-text">
|
|
||||||
${msg("Flow used when authorizing this provider.")}
|
|
||||||
</p>
|
|
||||||
</ak-form-element-horizontal>
|
|
||||||
|
|
||||||
<ak-file-input name="metadata" label=${msg("Metadata")} required></ak-file-input>
|
|
||||||
</form>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ApplicationWizardProviderSamlImport;
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
import "@goauthentik/admin/applications/wizard/ak-wizard-title";
|
||||||
|
import "@goauthentik/admin/common/ak-core-group-search";
|
||||||
import "@goauthentik/admin/common/ak-crypto-certificate-search";
|
import "@goauthentik/admin/common/ak-crypto-certificate-search";
|
||||||
import "@goauthentik/admin/common/ak-flow-search/ak-tenanted-flow-search";
|
import "@goauthentik/admin/common/ak-flow-search/ak-tenanted-flow-search";
|
||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import { first } from "@goauthentik/common/utils";
|
import { first } from "@goauthentik/common/utils";
|
||||||
|
import "@goauthentik/components/ak-multi-select";
|
||||||
import "@goauthentik/components/ak-switch-input";
|
import "@goauthentik/components/ak-switch-input";
|
||||||
import "@goauthentik/components/ak-text-input";
|
import "@goauthentik/components/ak-text-input";
|
||||||
import "@goauthentik/elements/forms/FormGroup";
|
import "@goauthentik/elements/forms/FormGroup";
|
||||||
|
@ -12,14 +15,7 @@ import { customElement, state } from "@lit/reactive-element/decorators.js";
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
import {
|
import { PaginatedSCIMMappingList, PropertymappingsApi, type SCIMProvider } from "@goauthentik/api";
|
||||||
CoreApi,
|
|
||||||
CoreGroupsListRequest,
|
|
||||||
type Group,
|
|
||||||
PaginatedSCIMMappingList,
|
|
||||||
PropertymappingsApi,
|
|
||||||
type SCIMProvider,
|
|
||||||
} from "@goauthentik/api";
|
|
||||||
|
|
||||||
import BaseProviderPanel from "../BaseProviderPanel";
|
import BaseProviderPanel from "../BaseProviderPanel";
|
||||||
|
|
||||||
|
@ -31,22 +27,52 @@ export class ApplicationWizardAuthenticationBySCIM extends BaseProviderPanel {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
new PropertymappingsApi(DEFAULT_CONFIG)
|
new PropertymappingsApi(DEFAULT_CONFIG)
|
||||||
.propertymappingsScopeList({
|
.propertymappingsScimList({
|
||||||
ordering: "scope_name",
|
ordering: "managed",
|
||||||
})
|
})
|
||||||
.then((propertyMappings: PaginatedSCIMMappingList) => {
|
.then((propertyMappings: PaginatedSCIMMappingList) => {
|
||||||
this.propertyMappings = propertyMappings;
|
this.propertyMappings = propertyMappings;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
propertyMappingConfiguration(provider?: SCIMProvider) {
|
||||||
|
const propertyMappings = this.propertyMappings?.results ?? [];
|
||||||
|
|
||||||
|
const configuredMappings = (providerMappings: string[]) =>
|
||||||
|
propertyMappings.map((pm) => pm.pk).filter((pmpk) => providerMappings.includes(pmpk));
|
||||||
|
|
||||||
|
const managedMappings = (key: string) =>
|
||||||
|
propertyMappings
|
||||||
|
.filter((pm) => pm.managed === `goauthentik.io/providers/scim/${key}`)
|
||||||
|
.map((pm) => pm.pk);
|
||||||
|
|
||||||
|
const pmUserValues = provider?.propertyMappings
|
||||||
|
? configuredMappings(provider?.propertyMappings ?? [])
|
||||||
|
: managedMappings("user");
|
||||||
|
|
||||||
|
const pmGroupValues = provider?.propertyMappingsGroup
|
||||||
|
? configuredMappings(provider?.propertyMappingsGroup ?? [])
|
||||||
|
: managedMappings("group");
|
||||||
|
|
||||||
|
const propertyPairs = propertyMappings.map((pm) => [pm.pk, pm.name]);
|
||||||
|
|
||||||
|
return { pmUserValues, pmGroupValues, propertyPairs };
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const provider = this.wizard.provider as SCIMProvider | undefined;
|
const provider = this.wizard.provider as SCIMProvider | undefined;
|
||||||
|
const errors = this.wizard.errors.provider;
|
||||||
|
|
||||||
return html`<form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
const { pmUserValues, pmGroupValues, propertyPairs } =
|
||||||
|
this.propertyMappingConfiguration(provider);
|
||||||
|
|
||||||
|
return html`<ak-wizard-title>${msg("Configure SCIM Provider")}</ak-wizard-title>
|
||||||
|
<form class="pf-c-form pf-m-horizontal" @input=${this.handleChange}>
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
name="name"
|
name="name"
|
||||||
label=${msg("Name")}
|
label=${msg("Name")}
|
||||||
value=${ifDefined(provider?.name)}
|
value=${ifDefined(provider?.name)}
|
||||||
|
.errorMessages=${errors?.name ?? []}
|
||||||
required
|
required
|
||||||
></ak-text-input>
|
></ak-text-input>
|
||||||
<ak-form-group expanded>
|
<ak-form-group expanded>
|
||||||
|
@ -58,12 +84,14 @@ export class ApplicationWizardAuthenticationBySCIM extends BaseProviderPanel {
|
||||||
value="${first(provider?.url, "")}"
|
value="${first(provider?.url, "")}"
|
||||||
required
|
required
|
||||||
help=${msg("SCIM base url, usually ends in /v2.")}
|
help=${msg("SCIM base url, usually ends in /v2.")}
|
||||||
|
.errorMessages=${errors?.url ?? []}
|
||||||
>
|
>
|
||||||
</ak-text-input>
|
</ak-text-input>
|
||||||
<ak-text-input
|
<ak-text-input
|
||||||
name="token"
|
name="token"
|
||||||
label=${msg("Token")}
|
label=${msg("Token")}
|
||||||
value="${first(provider?.token, "")}"
|
value="${first(provider?.token, "")}"
|
||||||
|
.errorMessages=${errors?.token ?? []}
|
||||||
required
|
required
|
||||||
help=${msg(
|
help=${msg(
|
||||||
"Token to authenticate with. Currently only bearer authentication is supported.",
|
"Token to authenticate with. Currently only bearer authentication is supported.",
|
||||||
|
@ -81,31 +109,9 @@ export class ApplicationWizardAuthenticationBySCIM extends BaseProviderPanel {
|
||||||
label=${msg("Exclude service accounts")}
|
label=${msg("Exclude service accounts")}
|
||||||
></ak-switch-input>
|
></ak-switch-input>
|
||||||
<ak-form-element-horizontal label=${msg("Group")} name="filterGroup">
|
<ak-form-element-horizontal label=${msg("Group")} name="filterGroup">
|
||||||
<ak-search-select
|
<ak-core-group-search
|
||||||
.fetchObjects=${async (query?: string): Promise<Group[]> => {
|
.group=${provider?.filterGroup}
|
||||||
const args: CoreGroupsListRequest = {
|
></ak-core-group-search>
|
||||||
ordering: "name",
|
|
||||||
};
|
|
||||||
if (query !== undefined) {
|
|
||||||
args.search = query;
|
|
||||||
}
|
|
||||||
const groups = await new CoreApi(DEFAULT_CONFIG).coreGroupsList(
|
|
||||||
args,
|
|
||||||
);
|
|
||||||
return groups.results;
|
|
||||||
}}
|
|
||||||
.renderElement=${(group: Group): string => {
|
|
||||||
return group.name;
|
|
||||||
}}
|
|
||||||
.value=${(group: Group | undefined): string | undefined => {
|
|
||||||
return group ? group.pk : undefined;
|
|
||||||
}}
|
|
||||||
.selected=${(group: Group): boolean => {
|
|
||||||
return group.pk === provider?.filterGroup;
|
|
||||||
}}
|
|
||||||
?blankable=${true}
|
|
||||||
>
|
|
||||||
</ak-search-select>
|
|
||||||
<p class="pf-c-form__helper-text">
|
<p class="pf-c-form__helper-text">
|
||||||
${msg("Only sync users within the selected group.")}
|
${msg("Only sync users within the selected group.")}
|
||||||
</p>
|
</p>
|
||||||
|
@ -115,71 +121,32 @@ export class ApplicationWizardAuthenticationBySCIM extends BaseProviderPanel {
|
||||||
<ak-form-group ?expanded=${true}>
|
<ak-form-group ?expanded=${true}>
|
||||||
<span slot="header"> ${msg("Attribute mapping")} </span>
|
<span slot="header"> ${msg("Attribute mapping")} </span>
|
||||||
<div slot="body" class="pf-c-form">
|
<div slot="body" class="pf-c-form">
|
||||||
<ak-form-element-horizontal
|
<ak-multi-select
|
||||||
label=${msg("User Property Mappings")}
|
label=${msg("User Property Mappings")}
|
||||||
?required=${true}
|
required
|
||||||
name="propertyMappings"
|
name="propertyMappings"
|
||||||
>
|
.options=${propertyPairs}
|
||||||
<select class="pf-c-form-control" multiple>
|
.values=${pmUserValues}
|
||||||
${this.propertyMappings?.results.map((mapping) => {
|
.richhelp=${html` <p class="pf-c-form__helper-text">
|
||||||
let selected = false;
|
${msg("Property mappings used for user mapping.")}
|
||||||
if (!provider?.propertyMappings) {
|
|
||||||
selected =
|
|
||||||
mapping.managed === "goauthentik.io/providers/scim/user" ||
|
|
||||||
false;
|
|
||||||
} else {
|
|
||||||
selected = Array.from(provider?.propertyMappings).some((su) => {
|
|
||||||
return su == mapping.pk;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return html`<option
|
|
||||||
value=${ifDefined(mapping.pk)}
|
|
||||||
?selected=${selected}
|
|
||||||
>
|
|
||||||
${mapping.name}
|
|
||||||
</option>`;
|
|
||||||
})}
|
|
||||||
</select>
|
|
||||||
<p class="pf-c-form__helper-text">
|
|
||||||
${msg("Property mappings used to user mapping.")}
|
|
||||||
</p>
|
</p>
|
||||||
<p class="pf-c-form__helper-text">
|
<p class="pf-c-form__helper-text">
|
||||||
${msg("Hold control/command to select multiple items.")}
|
${msg("Hold control/command to select multiple items.")}
|
||||||
</p>
|
</p>`}
|
||||||
</ak-form-element-horizontal>
|
></ak-multi-select>
|
||||||
<ak-form-element-horizontal
|
<ak-multi-select
|
||||||
label=${msg("Group Property Mappings")}
|
label=${msg("Group Property Mappings")}
|
||||||
?required=${true}
|
required
|
||||||
name="propertyMappingsGroup"
|
name="propertyMappingsGroup"
|
||||||
>
|
.options=${propertyPairs}
|
||||||
<select class="pf-c-form-control" multiple>
|
.values=${pmGroupValues}
|
||||||
${this.propertyMappings?.results.map((mapping) => {
|
.richhelp=${html` <p class="pf-c-form__helper-text">
|
||||||
let selected = false;
|
${msg("Property mappings used for group creation.")}
|
||||||
if (!provider?.propertyMappingsGroup) {
|
|
||||||
selected =
|
|
||||||
mapping.managed === "goauthentik.io/providers/scim/group";
|
|
||||||
} else {
|
|
||||||
selected = Array.from(provider?.propertyMappingsGroup).some(
|
|
||||||
(su) => {
|
|
||||||
return su == mapping.pk;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return html`<option
|
|
||||||
value=${ifDefined(mapping.pk)}
|
|
||||||
?selected=${selected}
|
|
||||||
>
|
|
||||||
${mapping.name}
|
|
||||||
</option>`;
|
|
||||||
})}
|
|
||||||
</select>
|
|
||||||
<p class="pf-c-form__helper-text">
|
|
||||||
${msg("Property mappings used to group creation.")}
|
|
||||||
</p>
|
</p>
|
||||||
<p class="pf-c-form__helper-text">
|
<p class="pf-c-form__helper-text">
|
||||||
${msg("Hold control/command to select multiple items.")}
|
${msg("Hold control/command to select multiple items.")}
|
||||||
</p>
|
</p>`}
|
||||||
</ak-form-element-horizontal>
|
></ak-multi-select>
|
||||||
</div>
|
</div>
|
||||||
</ak-form-group>
|
</ak-form-group>
|
||||||
</form>`;
|
</form>`;
|
||||||
|
|
|
@ -15,6 +15,12 @@ import "./commit/ak-application-wizard-commit-application";
|
||||||
import "./methods/ak-application-wizard-authentication-method";
|
import "./methods/ak-application-wizard-authentication-method";
|
||||||
import { ApplicationStep as ApplicationStepType } from "./types";
|
import { ApplicationStep as ApplicationStepType } from "./types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In the current implementation, all of the child forms have access to the wizard's
|
||||||
|
* global context, into which all data is written, and which is updated by events
|
||||||
|
* flowing into the top-level orchestrator.
|
||||||
|
*/
|
||||||
|
|
||||||
class ApplicationStep implements ApplicationStepType {
|
class ApplicationStep implements ApplicationStepType {
|
||||||
id = "application";
|
id = "application";
|
||||||
label = "Application Details";
|
label = "Application Details";
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { customElement } from "@lit/reactive-element/decorators/custom-element.j
|
||||||
import { state } from "@lit/reactive-element/decorators/state.js";
|
import { state } from "@lit/reactive-element/decorators/state.js";
|
||||||
import { LitElement, html } from "lit";
|
import { LitElement, html } from "lit";
|
||||||
|
|
||||||
import applicationWizardContext from "../ContextIdentity";
|
import { applicationWizardContext } from "../ContextIdentity";
|
||||||
import type { ApplicationWizardState } from "../types";
|
import type { ApplicationWizardState } from "../types";
|
||||||
|
|
||||||
@customElement("ak-application-context-display-for-test")
|
@customElement("ak-application-context-display-for-test")
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
type RadiusProviderRequest,
|
type RadiusProviderRequest,
|
||||||
type SAMLProviderRequest,
|
type SAMLProviderRequest,
|
||||||
type SCIMProviderRequest,
|
type SCIMProviderRequest,
|
||||||
|
type ValidationError,
|
||||||
} from "@goauthentik/api";
|
} from "@goauthentik/api";
|
||||||
|
|
||||||
export type OneOfProvider =
|
export type OneOfProvider =
|
||||||
|
@ -24,12 +25,13 @@ export interface ApplicationWizardState {
|
||||||
providerModel: string;
|
providerModel: string;
|
||||||
app: Partial<ApplicationRequest>;
|
app: Partial<ApplicationRequest>;
|
||||||
provider: OneOfProvider;
|
provider: OneOfProvider;
|
||||||
|
errors: ValidationError;
|
||||||
}
|
}
|
||||||
|
|
||||||
type StatusType = "invalid" | "valid" | "submitted" | "failed";
|
type StatusType = "invalid" | "valid" | "submitted" | "failed";
|
||||||
|
|
||||||
export type ApplicationWizardStateUpdate = {
|
export type ApplicationWizardStateUpdate = {
|
||||||
update?: Partial<ApplicationWizardState>;
|
update?: ApplicationWizardState;
|
||||||
status?: StatusType;
|
status?: StatusType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import "@goauthentik/admin/events/EventVolumeChart";
|
||||||
import { EventGeo } from "@goauthentik/admin/events/utils";
|
import { EventGeo } from "@goauthentik/admin/events/utils";
|
||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
import { EventWithContext } from "@goauthentik/common/events";
|
import { EventWithContext } from "@goauthentik/common/events";
|
||||||
|
@ -10,7 +11,7 @@ import { TablePage } from "@goauthentik/elements/table/TablePage";
|
||||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
||||||
|
|
||||||
import { msg, str } from "@lit/localize";
|
import { msg, str } from "@lit/localize";
|
||||||
import { TemplateResult, html } from "lit";
|
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
|
|
||||||
import { Event, EventsApi } from "@goauthentik/api";
|
import { Event, EventsApi } from "@goauthentik/api";
|
||||||
|
@ -35,6 +36,14 @@ export class EventListPage extends TablePage<Event> {
|
||||||
@property()
|
@property()
|
||||||
order = "-created";
|
order = "-created";
|
||||||
|
|
||||||
|
static get styles(): CSSResult[] {
|
||||||
|
return super.styles.concat(css`
|
||||||
|
.pf-m-no-padding-bottom {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
async apiEndpoint(page: number): Promise<PaginatedResponse<Event>> {
|
async apiEndpoint(page: number): Promise<PaginatedResponse<Event>> {
|
||||||
return new EventsApi(DEFAULT_CONFIG).eventsEventsList({
|
return new EventsApi(DEFAULT_CONFIG).eventsEventsList({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
|
@ -55,6 +64,19 @@ export class EventListPage extends TablePage<Event> {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderSectionBefore(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="pf-c-page__main-section pf-m-no-padding-bottom">
|
||||||
|
<ak-events-volume-chart
|
||||||
|
.query=${{
|
||||||
|
page: this.page,
|
||||||
|
search: this.search,
|
||||||
|
}}
|
||||||
|
></ak-events-volume-chart>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
row(item: EventWithContext): TemplateResult[] {
|
row(item: EventWithContext): TemplateResult[] {
|
||||||
return [
|
return [
|
||||||
html`<div>${actionToLabel(item.action)}</div>
|
html`<div>${actionToLabel(item.action)}</div>
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { DEFAULT_CONFIG } from "@goauthentik/app/common/api/config";
|
||||||
|
import { AKChart } from "@goauthentik/app/elements/charts/Chart";
|
||||||
|
import { ChartData } from "chart.js";
|
||||||
|
|
||||||
|
import { msg } from "@lit/localize";
|
||||||
|
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators.js";
|
||||||
|
|
||||||
|
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||||
|
|
||||||
|
import { Coordinate, EventsApi, EventsEventsListRequest } from "@goauthentik/api";
|
||||||
|
|
||||||
|
@customElement("ak-events-volume-chart")
|
||||||
|
export class EventVolumeChart extends AKChart<Coordinate[]> {
|
||||||
|
_query?: EventsEventsListRequest;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
set query(value: EventsEventsListRequest | undefined) {
|
||||||
|
this._query = value;
|
||||||
|
this.refreshHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult[] {
|
||||||
|
return super.styles.concat(
|
||||||
|
PFCard,
|
||||||
|
css`
|
||||||
|
.pf-c-card__body {
|
||||||
|
height: 12rem;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
apiRequest(): Promise<Coordinate[]> {
|
||||||
|
return new EventsApi(DEFAULT_CONFIG).eventsEventsVolumeList(this._query);
|
||||||
|
}
|
||||||
|
|
||||||
|
getChartData(data: Coordinate[]): ChartData {
|
||||||
|
return {
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: msg("Events"),
|
||||||
|
backgroundColor: "rgba(189, 229, 184, .5)",
|
||||||
|
spanGaps: true,
|
||||||
|
data:
|
||||||
|
data.map((cord) => {
|
||||||
|
return {
|
||||||
|
x: cord.xCord || 0,
|
||||||
|
y: cord.yCord || 0,
|
||||||
|
};
|
||||||
|
}) || [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): TemplateResult {
|
||||||
|
return html`<div class="pf-c-card">
|
||||||
|
<div class="pf-c-card__title">${msg("Event volume")}</div>
|
||||||
|
<div class="pf-c-card__body">${super.render()}</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,120 +0,0 @@
|
||||||
/** Taken from: https://github.com/zellwk/javascript/tree/master
|
|
||||||
*
|
|
||||||
* We have added some typescript annotations, but this is such a rich feature with deep nesting
|
|
||||||
* we'll just have to watch it closely for any issues. So far there don't seem to be any.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
function objectType<T>(value: T) {
|
|
||||||
return Object.prototype.toString.call(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a deep clone for each value
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
function cloneDescriptorValue(value: any) {
|
|
||||||
// Arrays
|
|
||||||
if (objectType(value) === "[object Array]") {
|
|
||||||
const array = [];
|
|
||||||
for (let v of value) {
|
|
||||||
v = cloneDescriptorValue(v);
|
|
||||||
array.push(v);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Objects
|
|
||||||
if (objectType(value) === "[object Object]") {
|
|
||||||
const obj = {};
|
|
||||||
const props = Object.keys(value);
|
|
||||||
for (const prop of props) {
|
|
||||||
const descriptor = Object.getOwnPropertyDescriptor(value, prop);
|
|
||||||
if (!descriptor) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (descriptor.value) {
|
|
||||||
descriptor.value = cloneDescriptorValue(descriptor.value);
|
|
||||||
}
|
|
||||||
Object.defineProperty(obj, prop, descriptor);
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Other Types of Objects
|
|
||||||
if (objectType(value) === "[object Date]") {
|
|
||||||
return new Date(value.getTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objectType(value) === "[object Map]") {
|
|
||||||
const map = new Map();
|
|
||||||
for (const entry of value) {
|
|
||||||
map.set(entry[0], cloneDescriptorValue(entry[1]));
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objectType(value) === "[object Set]") {
|
|
||||||
const set = new Set();
|
|
||||||
for (const entry of value.entries()) {
|
|
||||||
set.add(cloneDescriptorValue(entry[0]));
|
|
||||||
}
|
|
||||||
return set;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Types we don't need to clone or cannot clone.
|
|
||||||
// Examples:
|
|
||||||
// - Primitives don't need to clone
|
|
||||||
// - Functions cannot clone
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
function _merge(output: Record<string, any>, input: Record<string, any>) {
|
|
||||||
const props = Object.keys(input);
|
|
||||||
|
|
||||||
for (const prop of props) {
|
|
||||||
// Prevents Prototype Pollution
|
|
||||||
if (prop === "__proto__") continue;
|
|
||||||
|
|
||||||
const descriptor = Object.getOwnPropertyDescriptor(input, prop);
|
|
||||||
if (!descriptor) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const value = descriptor.value;
|
|
||||||
if (value) descriptor.value = cloneDescriptorValue(value);
|
|
||||||
|
|
||||||
// If don't have prop => Define property
|
|
||||||
// [ken@goauthentik] Using `hasOwn` is preferable over
|
|
||||||
// the basic identity test, according to Typescript.
|
|
||||||
if (!Object.hasOwn(output, prop)) {
|
|
||||||
Object.defineProperty(output, prop, descriptor);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If have prop, but type is not object => Overwrite by redefining property
|
|
||||||
if (typeof output[prop] !== "object") {
|
|
||||||
Object.defineProperty(output, prop, descriptor);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If have prop, but type is Object => Concat the arrays together.
|
|
||||||
if (objectType(descriptor.value) === "[object Array]") {
|
|
||||||
output[prop] = output[prop].concat(descriptor.value);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If have prop, but type is Object => Merge.
|
|
||||||
_merge(output[prop], descriptor.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function merge(...sources: Array<object>) {
|
|
||||||
const result = {};
|
|
||||||
for (const source of sources) {
|
|
||||||
_merge(result, source);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default merge;
|
|
|
@ -54,6 +54,13 @@ export function camelToSnake(key: string): string {
|
||||||
return result.split(" ").join("_").toLowerCase();
|
return result.split(" ").join("_").toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const capitalize = (key: string) => (key.length === 0 ? "" : key[0].toUpperCase() + key.slice(1));
|
||||||
|
|
||||||
|
export function snakeToCamel(key: string) {
|
||||||
|
const [start, ...rest] = key.split("_");
|
||||||
|
return [start, ...rest.map(capitalize)].join("");
|
||||||
|
}
|
||||||
|
|
||||||
export function groupBy<T>(objects: T[], callback: (obj: T) => string): Array<[string, T[]]> {
|
export function groupBy<T>(objects: T[], callback: (obj: T) => string): Array<[string, T[]]> {
|
||||||
const m = new Map<string, T[]>();
|
const m = new Map<string, T[]>();
|
||||||
objects.forEach((obj) => {
|
objects.forEach((obj) => {
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
|
|
||||||
|
import { TemplateResult, html, nothing } from "lit";
|
||||||
|
import { property } from "lit/decorators.js";
|
||||||
|
|
||||||
|
type HelpType = TemplateResult | typeof nothing;
|
||||||
|
|
||||||
|
export class HorizontalLightComponent extends AKElement {
|
||||||
|
// Render into the lightDOM. This effectively erases the shadowDOM nature of this component, but
|
||||||
|
// we're not actually using that and, for the meantime, we need the form handlers to be able to
|
||||||
|
// find the children of this component.
|
||||||
|
//
|
||||||
|
// TODO: This abstraction is wrong; it's putting *more* layers in as a way of managing the
|
||||||
|
// visual clutter and legibility issues of ak-form-elemental-horizontal and patternfly in
|
||||||
|
// general.
|
||||||
|
protected createRenderRoot() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property({ type: String })
|
||||||
|
name!: string;
|
||||||
|
|
||||||
|
@property({ type: String })
|
||||||
|
label = "";
|
||||||
|
|
||||||
|
@property({ type: Boolean })
|
||||||
|
required = false;
|
||||||
|
|
||||||
|
@property({ type: String })
|
||||||
|
help = "";
|
||||||
|
|
||||||
|
@property({ type: Object })
|
||||||
|
bighelp?: TemplateResult | TemplateResult[];
|
||||||
|
|
||||||
|
@property({ type: Boolean })
|
||||||
|
hidden = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean })
|
||||||
|
invalid = false;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
errorMessages: string[] = [];
|
||||||
|
|
||||||
|
renderControl() {
|
||||||
|
throw new Error("Must be implemented in a subclass");
|
||||||
|
}
|
||||||
|
|
||||||
|
renderHelp(): HelpType[] {
|
||||||
|
const bigHelp: HelpType[] = Array.isArray(this.bighelp)
|
||||||
|
? this.bighelp
|
||||||
|
: [this.bighelp ?? nothing];
|
||||||
|
return [
|
||||||
|
this.help ? html`<p class="pf-c-form__helper-text">${this.help}</p>` : nothing,
|
||||||
|
...bigHelp,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
// prettier-ignore
|
||||||
|
return html`<ak-form-element-horizontal
|
||||||
|
label=${this.label}
|
||||||
|
?required=${this.required}
|
||||||
|
?hidden=${this.hidden}
|
||||||
|
name=${this.name}
|
||||||
|
.errorMessages=${this.errorMessages}
|
||||||
|
?invalid=${this.invalid}
|
||||||
|
>
|
||||||
|
${this.renderControl()}
|
||||||
|
${this.renderHelp()}
|
||||||
|
</ak-form-element-horizontal> `;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
import "@goauthentik/app/elements/forms/HorizontalFormElement";
|
||||||
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
|
|
||||||
|
import { TemplateResult, css, html, nothing } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators.js";
|
||||||
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
import { map } from "lit/directives/map.js";
|
||||||
|
import { Ref, createRef, ref } from "lit/directives/ref.js";
|
||||||
|
|
||||||
|
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||||
|
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||||
|
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
|
|
||||||
|
type Pair = [string, string];
|
||||||
|
|
||||||
|
const selectStyles = css`
|
||||||
|
select[multiple] {
|
||||||
|
min-height: 15rem;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Horizontal layout control with a multi-select.
|
||||||
|
*
|
||||||
|
* @part select - The select itself, to override the height specified above.
|
||||||
|
*/
|
||||||
|
@customElement("ak-multi-select")
|
||||||
|
export class AkMultiSelect extends AKElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.dataset.akControl = "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return [PFBase, PFForm, PFFormControl, selectStyles];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [name] attribute, which is also distributed to the layout manager and the input control.
|
||||||
|
*/
|
||||||
|
@property({ type: String })
|
||||||
|
name!: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The text label to display on the control
|
||||||
|
*/
|
||||||
|
@property({ type: String })
|
||||||
|
label = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The values to be displayed in the select. The format is [Value, Label], where the label is
|
||||||
|
* what will be displayed.
|
||||||
|
*/
|
||||||
|
@property({ attribute: false })
|
||||||
|
options: Pair[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, at least one object must be selected
|
||||||
|
*/
|
||||||
|
@property({ type: Boolean })
|
||||||
|
required = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supporting a simple help string
|
||||||
|
*/
|
||||||
|
@property({ type: String })
|
||||||
|
help = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For more complex help instructions, provide a template result.
|
||||||
|
*/
|
||||||
|
@property({ type: Object })
|
||||||
|
bighelp!: TemplateResult | TemplateResult[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of strings representing the objects currently selected.
|
||||||
|
*/
|
||||||
|
@property({ type: Array })
|
||||||
|
values: string[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper accessor for older code
|
||||||
|
*/
|
||||||
|
get value() {
|
||||||
|
return this.values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One of two criteria (the other being the data-ak-control flag) that specifies this as a
|
||||||
|
* control that produces values of specific interest to our REST API. This is our modern
|
||||||
|
* accessor name.
|
||||||
|
*/
|
||||||
|
json() {
|
||||||
|
return this.values;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderHelp() {
|
||||||
|
return [
|
||||||
|
this.help ? html`<p class="pf-c-form__helper-text">${this.help}</p>` : nothing,
|
||||||
|
this.bighelp ? this.bighelp : nothing,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChange(ev: Event) {
|
||||||
|
if (ev.type === "change") {
|
||||||
|
this.values = Array.from(this.selectRef.value!.querySelectorAll("option"))
|
||||||
|
.filter((option) => option.selected)
|
||||||
|
.map((option) => option.value);
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent("ak-select", {
|
||||||
|
detail: this.values,
|
||||||
|
composed: true,
|
||||||
|
bubbles: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectRef: Ref<HTMLSelectElement> = createRef();
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html` <div class="pf-c-form">
|
||||||
|
<ak-form-element-horizontal
|
||||||
|
label=${this.label}
|
||||||
|
?required=${this.required}
|
||||||
|
name=${this.name}
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
part="select"
|
||||||
|
class="pf-c-form-control"
|
||||||
|
name=${ifDefined(this.name)}
|
||||||
|
multiple
|
||||||
|
${ref(this.selectRef)}
|
||||||
|
@change=${this.handleChange}
|
||||||
|
>
|
||||||
|
${map(
|
||||||
|
this.options,
|
||||||
|
([value, label]) =>
|
||||||
|
html`<option value=${value} ?selected=${this.values.includes(value)}>
|
||||||
|
${label}
|
||||||
|
</option>`,
|
||||||
|
)}
|
||||||
|
</select>
|
||||||
|
${this.renderHelp()}
|
||||||
|
</ak-form-element-horizontal>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AkMultiSelect;
|
|
@ -1,51 +1,21 @@
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { html } from "lit";
|
||||||
|
|
||||||
import { html, nothing } from "lit";
|
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
|
import { HorizontalLightComponent } from "./HorizontalLightComponent";
|
||||||
|
|
||||||
@customElement("ak-number-input")
|
@customElement("ak-number-input")
|
||||||
export class AkNumberInput extends AKElement {
|
export class AkNumberInput extends HorizontalLightComponent {
|
||||||
// Render into the lightDOM. This effectively erases the shadowDOM nature of this component, but
|
|
||||||
// we're not actually using that and, for the meantime, we need the form handlers to be able to
|
|
||||||
// find the children of this component.
|
|
||||||
//
|
|
||||||
// TODO: This abstraction is wrong; it's putting *more* layers in as a way of managing the
|
|
||||||
// visual clutter and legibility issues of ak-form-elemental-horizontal and patternfly in
|
|
||||||
// general.
|
|
||||||
protected createRenderRoot() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
name!: string;
|
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
label = "";
|
|
||||||
|
|
||||||
@property({ type: Number, reflect: true })
|
@property({ type: Number, reflect: true })
|
||||||
value = 0;
|
value = 0;
|
||||||
|
|
||||||
@property({ type: Boolean })
|
renderControl() {
|
||||||
required = false;
|
return html`<input
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
help = "";
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html`<ak-form-element-horizontal
|
|
||||||
label=${this.label}
|
|
||||||
?required=${this.required}
|
|
||||||
name=${this.name}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="number"
|
type="number"
|
||||||
value=${ifDefined(this.value)}
|
value=${ifDefined(this.value)}
|
||||||
class="pf-c-form-control"
|
class="pf-c-form-control"
|
||||||
?required=${this.required}
|
?required=${this.required}
|
||||||
/>
|
/>`;
|
||||||
${this.help ? html`<p class="pf-c-form__helper-text">${this.help}</p>` : nothing}
|
|
||||||
</ak-form-element-horizontal> `;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,35 +1,13 @@
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
|
||||||
import { RadioOption } from "@goauthentik/elements/forms/Radio";
|
import { RadioOption } from "@goauthentik/elements/forms/Radio";
|
||||||
import "@goauthentik/elements/forms/Radio";
|
import "@goauthentik/elements/forms/Radio";
|
||||||
|
|
||||||
import { html, nothing } from "lit";
|
import { html, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
|
|
||||||
|
import { HorizontalLightComponent } from "./HorizontalLightComponent";
|
||||||
|
|
||||||
@customElement("ak-radio-input")
|
@customElement("ak-radio-input")
|
||||||
export class AkRadioInput<T> extends AKElement {
|
export class AkRadioInput<T> extends HorizontalLightComponent {
|
||||||
// Render into the lightDOM. This effectively erases the shadowDOM nature of this component, but
|
|
||||||
// we're not actually using that and, for the meantime, we need the form handlers to be able to
|
|
||||||
// find the children of this component.
|
|
||||||
//
|
|
||||||
// TODO: This abstraction is wrong; it's putting *more* layers in as a way of managing the
|
|
||||||
// visual clutter and legibility issues of ak-form-elemental-horizontal and patternfly in
|
|
||||||
// general.
|
|
||||||
protected createRenderRoot() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
name!: string;
|
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
label = "";
|
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
help = "";
|
|
||||||
|
|
||||||
@property({ type: Boolean })
|
|
||||||
required = false;
|
|
||||||
|
|
||||||
@property({ type: Object })
|
@property({ type: Object })
|
||||||
value!: T;
|
value!: T;
|
||||||
|
|
||||||
|
@ -37,24 +15,25 @@ export class AkRadioInput<T> extends AKElement {
|
||||||
options: RadioOption<T>[] = [];
|
options: RadioOption<T>[] = [];
|
||||||
|
|
||||||
handleInput(ev: CustomEvent) {
|
handleInput(ev: CustomEvent) {
|
||||||
|
if ("detail" in ev) {
|
||||||
this.value = ev.detail.value;
|
this.value = ev.detail.value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
renderHelp() {
|
||||||
return html`<ak-form-element-horizontal
|
// This is weird, but Typescript says it's necessary?
|
||||||
label=${this.label}
|
return [nothing as typeof nothing];
|
||||||
?required=${this.required}
|
}
|
||||||
name=${this.name}
|
|
||||||
>
|
renderControl() {
|
||||||
<ak-radio
|
return html`<ak-radio
|
||||||
.options=${this.options}
|
.options=${this.options}
|
||||||
.value=${this.value}
|
.value=${this.value}
|
||||||
@input=${this.handleInput}
|
@input=${this.handleInput}
|
||||||
></ak-radio>
|
></ak-radio>
|
||||||
${this.help.trim()
|
${this.help.trim()
|
||||||
? html`<p class="pf-c-form__helper-radio">${this.help}</p>`
|
? html`<p class="pf-c-form__helper-radio">${this.help}</p>`
|
||||||
: nothing}
|
: nothing}`;
|
||||||
</ak-form-element-horizontal> `;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,44 +1,16 @@
|
||||||
import { convertToSlug } from "@goauthentik/common/utils";
|
import { convertToSlug } from "@goauthentik/common/utils";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
|
||||||
|
|
||||||
import { TemplateResult, html, nothing } from "lit";
|
import { html } from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators.js";
|
import { customElement, property, query } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
|
import { HorizontalLightComponent } from "./HorizontalLightComponent";
|
||||||
|
|
||||||
@customElement("ak-slug-input")
|
@customElement("ak-slug-input")
|
||||||
export class AkSlugInput extends AKElement {
|
export class AkSlugInput extends HorizontalLightComponent {
|
||||||
// Render into the lightDOM. This effectively erases the shadowDOM nature of this component, but
|
|
||||||
// we're not actually using that and, for the meantime, we need the form handlers to be able to
|
|
||||||
// find the children of this component.
|
|
||||||
//
|
|
||||||
// TODO: This abstraction is wrong; it's putting *more* layers in as a way of managing the
|
|
||||||
// visual clutter and legibility issues of ak-form-elemental-horizontal and patternfly in
|
|
||||||
// general.
|
|
||||||
protected createRenderRoot() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
name!: string;
|
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
label = "";
|
|
||||||
|
|
||||||
@property({ type: String, reflect: true })
|
@property({ type: String, reflect: true })
|
||||||
value = "";
|
value = "";
|
||||||
|
|
||||||
@property({ type: Boolean })
|
|
||||||
required = false;
|
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
help = "";
|
|
||||||
|
|
||||||
@property({ type: Boolean })
|
|
||||||
hidden = false;
|
|
||||||
|
|
||||||
@property({ type: Object })
|
|
||||||
bighelp!: TemplateResult | TemplateResult[];
|
|
||||||
|
|
||||||
@property({ type: String })
|
@property({ type: String })
|
||||||
source = "";
|
source = "";
|
||||||
|
|
||||||
|
@ -59,13 +31,6 @@ export class AkSlugInput extends AKElement {
|
||||||
this.input.addEventListener("input", this.handleTouch);
|
this.input.addEventListener("input", this.handleTouch);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderHelp() {
|
|
||||||
return [
|
|
||||||
this.help ? html`<p class="pf-c-form__helper-text">${this.help}</p>` : nothing,
|
|
||||||
this.bighelp ? this.bighelp : nothing,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not stop propagation of this event; it must be sent up the tree so that a parent
|
// Do not stop propagation of this event; it must be sent up the tree so that a parent
|
||||||
// component, such as a custom forms manager, may receive it.
|
// component, such as a custom forms manager, may receive it.
|
||||||
handleTouch(ev: Event) {
|
handleTouch(ev: Event) {
|
||||||
|
@ -150,21 +115,13 @@ export class AkSlugInput extends AKElement {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
renderControl() {
|
||||||
return html`<ak-form-element-horizontal
|
return html`<input
|
||||||
label=${this.label}
|
|
||||||
?required=${this.required}
|
|
||||||
?hidden=${this.hidden}
|
|
||||||
name=${this.name}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="text"
|
type="text"
|
||||||
value=${ifDefined(this.value)}
|
value=${ifDefined(this.value)}
|
||||||
class="pf-c-form-control"
|
class="pf-c-form-control"
|
||||||
?required=${this.required}
|
?required=${this.required}
|
||||||
/>
|
/>`;
|
||||||
${this.renderHelp()}
|
|
||||||
</ak-form-element-horizontal> `;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,65 +1,21 @@
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { html } from "lit";
|
||||||
|
|
||||||
import { TemplateResult, html, nothing } from "lit";
|
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
|
import { HorizontalLightComponent } from "./HorizontalLightComponent";
|
||||||
|
|
||||||
@customElement("ak-text-input")
|
@customElement("ak-text-input")
|
||||||
export class AkTextInput extends AKElement {
|
export class AkTextInput extends HorizontalLightComponent {
|
||||||
// Render into the lightDOM. This effectively erases the shadowDOM nature of this component, but
|
|
||||||
// we're not actually using that and, for the meantime, we need the form handlers to be able to
|
|
||||||
// find the children of this component.
|
|
||||||
//
|
|
||||||
// TODO: This abstraction is wrong; it's putting *more* layers in as a way of managing the
|
|
||||||
// visual clutter and legibility issues of ak-form-elemental-horizontal and patternfly in
|
|
||||||
// general.
|
|
||||||
protected createRenderRoot() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
name!: string;
|
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
label = "";
|
|
||||||
|
|
||||||
@property({ type: String, reflect: true })
|
@property({ type: String, reflect: true })
|
||||||
value = "";
|
value = "";
|
||||||
|
|
||||||
@property({ type: Boolean })
|
renderControl() {
|
||||||
required = false;
|
return html` <input
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
help = "";
|
|
||||||
|
|
||||||
@property({ type: Boolean })
|
|
||||||
hidden = false;
|
|
||||||
|
|
||||||
@property({ type: Object })
|
|
||||||
bighelp!: TemplateResult | TemplateResult[];
|
|
||||||
|
|
||||||
renderHelp() {
|
|
||||||
return [
|
|
||||||
this.help ? html`<p class="pf-c-form__helper-text">${this.help}</p>` : nothing,
|
|
||||||
this.bighelp ? this.bighelp : nothing,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html`<ak-form-element-horizontal
|
|
||||||
label=${this.label}
|
|
||||||
?required=${this.required}
|
|
||||||
?hidden=${this.hidden}
|
|
||||||
name=${this.name}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="text"
|
type="text"
|
||||||
value=${ifDefined(this.value)}
|
value=${ifDefined(this.value)}
|
||||||
class="pf-c-form-control"
|
class="pf-c-form-control"
|
||||||
?required=${this.required}
|
?required=${this.required}
|
||||||
/>
|
/>`;
|
||||||
${this.renderHelp()}
|
|
||||||
</ak-form-element-horizontal> `;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,57 +1,22 @@
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { html } from "lit";
|
||||||
|
|
||||||
import { TemplateResult, html, nothing } from "lit";
|
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
|
|
||||||
|
import { HorizontalLightComponent } from "./HorizontalLightComponent";
|
||||||
|
|
||||||
@customElement("ak-textarea-input")
|
@customElement("ak-textarea-input")
|
||||||
export class AkTextareaInput extends AKElement {
|
export class AkTextareaInput extends HorizontalLightComponent {
|
||||||
// Render into the lightDOM. This effectively erases the shadowDOM nature of this component, but
|
@property({ type: String, reflect: true })
|
||||||
// we're not actually using that and, for the meantime, we need the form handlers to be able to
|
|
||||||
// find the children of this component.
|
|
||||||
//
|
|
||||||
// TODO: This abstraction is wrong; it's putting *more* layers in as a way of managing the
|
|
||||||
// visual clutter and legibility issues of ak-form-elemental-horizontal and patternfly in
|
|
||||||
// general.
|
|
||||||
protected createRenderRoot() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
name!: string;
|
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
label = "";
|
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
value = "";
|
value = "";
|
||||||
|
|
||||||
@property({ type: Boolean })
|
renderControl() {
|
||||||
required = false;
|
// Prevent the leading spaces added by Prettier's whitespace algo
|
||||||
|
// prettier-ignore
|
||||||
@property({ type: String })
|
return html`<textarea
|
||||||
help = "";
|
class="pf-c-form-control"
|
||||||
|
|
||||||
@property({ type: Object })
|
|
||||||
bighelp!: TemplateResult | TemplateResult[];
|
|
||||||
|
|
||||||
renderHelp() {
|
|
||||||
return [
|
|
||||||
this.help ? html`<p class="pf-c-form__helper-textarea">${this.help}</p>` : nothing,
|
|
||||||
this.bighelp ? this.bighelp : nothing,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html`<ak-form-element-horizontal
|
|
||||||
label=${this.label}
|
|
||||||
?required=${this.required}
|
?required=${this.required}
|
||||||
name=${this.name}
|
name=${this.name}
|
||||||
>
|
>${this.value !== undefined ? this.value : ""}</textarea
|
||||||
<textarea class="pf-c-form-control" ?required=${this.required} name=${this.name}>
|
> `;
|
||||||
${this.value !== undefined ? this.value : ""}</textarea
|
|
||||||
>
|
|
||||||
${this.renderHelp()}
|
|
||||||
</ak-form-element-horizontal> `;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
import "@goauthentik/elements/messages/MessageContainer";
|
||||||
|
import { Meta } from "@storybook/web-components";
|
||||||
|
|
||||||
|
import { TemplateResult, html, render } from "lit";
|
||||||
|
|
||||||
|
import "../ak-multi-select";
|
||||||
|
import AkMultiSelect from "../ak-multi-select";
|
||||||
|
|
||||||
|
const metadata: Meta<AkMultiSelect> = {
|
||||||
|
title: "Components / MultiSelect",
|
||||||
|
component: "ak-multi-select",
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component: "A stylized value control for multi-select displays",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default metadata;
|
||||||
|
|
||||||
|
const container = (testItem: TemplateResult) =>
|
||||||
|
html` <div style="background: #fff; padding: 2em">
|
||||||
|
<style>
|
||||||
|
li {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
${testItem}
|
||||||
|
|
||||||
|
<div id="message-pad" style="margin-top: 1em"></div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
const testOptions = [
|
||||||
|
["funky", "Option One: Funky"],
|
||||||
|
["strange", "Option Two: Strange"],
|
||||||
|
["weird", "Option Three: Weird"],
|
||||||
|
];
|
||||||
|
|
||||||
|
export const RadioInput = () => {
|
||||||
|
const result = "";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const displayChange = (ev: any) => {
|
||||||
|
const messagePad = document.getElementById("message-pad");
|
||||||
|
const component: AkMultiSelect | null = document.querySelector(
|
||||||
|
'ak-multi-select[name="ak-test-multi-select"]',
|
||||||
|
);
|
||||||
|
|
||||||
|
const results = html`
|
||||||
|
<p>Results from event:</p>
|
||||||
|
<ul style="list-style-type: disc">
|
||||||
|
${ev.target.value.map((v: string) => html`<li>${v}</li>`)}
|
||||||
|
</ul>
|
||||||
|
<p>Results from component:</p>
|
||||||
|
<ul style="list-style-type: disc">
|
||||||
|
${component!.json().map((v: string) => html`<li>${v}</li>`)}
|
||||||
|
</ul>
|
||||||
|
`;
|
||||||
|
|
||||||
|
render(results, messagePad!);
|
||||||
|
};
|
||||||
|
|
||||||
|
return container(
|
||||||
|
html`<ak-multi-select
|
||||||
|
@ak-select=${displayChange}
|
||||||
|
label="Test Radio Button"
|
||||||
|
name="ak-test-multi-select"
|
||||||
|
help="This is where you would read the help messages"
|
||||||
|
.options=${testOptions}
|
||||||
|
></ak-multi-select>
|
||||||
|
<div>${result}</div>`,
|
||||||
|
);
|
||||||
|
};
|
|
@ -31,10 +31,15 @@ export interface KeyUnknown {
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Literally the only field `assignValue()` cares about.
|
||||||
|
type HTMLNamedElement = Pick<HTMLInputElement, "name">;
|
||||||
|
|
||||||
|
type AkControlElement = HTMLInputElement & { json: () => string | string[] };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively assign `value` into `json` while interpreting the dot-path of `element.name`
|
* Recursively assign `value` into `json` while interpreting the dot-path of `element.name`
|
||||||
*/
|
*/
|
||||||
function assignValue(element: HTMLInputElement, value: unknown, json: KeyUnknown): void {
|
function assignValue(element: HTMLNamedElement, value: unknown, json: KeyUnknown): void {
|
||||||
let parent = json;
|
let parent = json;
|
||||||
if (!element.name?.includes(".")) {
|
if (!element.name?.includes(".")) {
|
||||||
parent[element.name] = value;
|
parent[element.name] = value;
|
||||||
|
@ -60,6 +65,16 @@ export function serializeForm<T extends KeyUnknown>(
|
||||||
const json: { [key: string]: unknown } = {};
|
const json: { [key: string]: unknown } = {};
|
||||||
elements.forEach((element) => {
|
elements.forEach((element) => {
|
||||||
element.requestUpdate();
|
element.requestUpdate();
|
||||||
|
if (element.hidden) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Tighten up the typing so that we can handle both.
|
||||||
|
if ("akControl" in element.dataset) {
|
||||||
|
assignValue(element, (element as unknown as AkControlElement).json(), json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const inputElement = element.querySelector<HTMLInputElement>("[name]");
|
const inputElement = element.querySelector<HTMLInputElement>("[name]");
|
||||||
if (element.hidden || !inputElement) {
|
if (element.hidden || !inputElement) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -18,7 +18,7 @@ import "@goauthentik/flow/stages/RedirectStage";
|
||||||
import { StageHost } from "@goauthentik/flow/stages/base";
|
import { StageHost } from "@goauthentik/flow/stages/base";
|
||||||
|
|
||||||
import { msg } from "@lit/localize";
|
import { msg } from "@lit/localize";
|
||||||
import { CSSResult, TemplateResult, css, html, render } from "lit";
|
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators.js";
|
import { customElement, property, state } from "lit/decorators.js";
|
||||||
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||||
import { until } from "lit/directives/until.js";
|
import { until } from "lit/directives/until.js";
|
||||||
|
@ -46,7 +46,8 @@ import {
|
||||||
|
|
||||||
@customElement("ak-flow-executor")
|
@customElement("ak-flow-executor")
|
||||||
export class FlowExecutor extends Interface implements StageHost {
|
export class FlowExecutor extends Interface implements StageHost {
|
||||||
flowSlug?: string;
|
@property()
|
||||||
|
flowSlug: string = window.location.pathname.split("/")[3];
|
||||||
|
|
||||||
private _challenge?: ChallengeTypes;
|
private _challenge?: ChallengeTypes;
|
||||||
|
|
||||||
|
@ -94,6 +95,9 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [PFBase, PFLogin, PFDrawer, PFButton, PFTitle, PFList, PFBackgroundImage].concat(css`
|
return [PFBase, PFLogin, PFDrawer, PFButton, PFTitle, PFList, PFBackgroundImage].concat(css`
|
||||||
|
:host {
|
||||||
|
--pf-c-login__main-body--PaddingBottom: var(--pf-global--spacer--2xl);
|
||||||
|
}
|
||||||
.pf-c-background-image::before {
|
.pf-c-background-image::before {
|
||||||
--pf-c-background-image--BackgroundImage: var(--ak-flow-background);
|
--pf-c-background-image--BackgroundImage: var(--ak-flow-background);
|
||||||
--pf-c-background-image--BackgroundImage-2x: var(--ak-flow-background);
|
--pf-c-background-image--BackgroundImage-2x: var(--ak-flow-background);
|
||||||
|
@ -111,6 +115,9 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
/* layouts */
|
/* layouts */
|
||||||
|
.pf-c-login.stacked .pf-c-login__main {
|
||||||
|
margin-top: 13rem;
|
||||||
|
}
|
||||||
.pf-c-login__container.content-right {
|
.pf-c-login__container.content-right {
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"header main"
|
"header main"
|
||||||
|
@ -146,13 +153,27 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||||
:host([theme="dark"]) .pf-c-login.sidebar_right .pf-c-list {
|
:host([theme="dark"]) .pf-c-login.sidebar_right .pf-c-list {
|
||||||
color: var(--ak-dark-foreground);
|
color: var(--ak-dark-foreground);
|
||||||
}
|
}
|
||||||
|
.pf-c-brand {
|
||||||
|
padding-top: calc(
|
||||||
|
var(--pf-c-login__main-footer-links--PaddingTop) +
|
||||||
|
var(--pf-c-login__main-footer-links--PaddingBottom) +
|
||||||
|
var(--pf-c-login__main-body--PaddingBottom)
|
||||||
|
);
|
||||||
|
max-height: 9rem;
|
||||||
|
}
|
||||||
|
.ak-brand {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.ak-brand img {
|
||||||
|
padding: 0 2rem;
|
||||||
|
}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.ws = new WebsocketClient();
|
this.ws = new WebsocketClient();
|
||||||
this.flowSlug = window.location.pathname.split("/")[3];
|
|
||||||
if (window.location.search.includes("inspector")) {
|
if (window.location.search.includes("inspector")) {
|
||||||
this.inspectorOpen = !this.inspectorOpen;
|
this.inspectorOpen = !this.inspectorOpen;
|
||||||
}
|
}
|
||||||
|
@ -165,55 +186,18 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||||
return globalAK()?.tenant.uiTheme || UiThemeEnum.Automatic;
|
return globalAK()?.tenant.uiTheme || UiThemeEnum.Automatic;
|
||||||
}
|
}
|
||||||
|
|
||||||
submit(payload?: FlowChallengeResponseRequest): Promise<boolean> {
|
async submit(payload?: FlowChallengeResponseRequest): Promise<boolean> {
|
||||||
if (!payload) return Promise.reject();
|
if (!payload) return Promise.reject();
|
||||||
if (!this.challenge) return Promise.reject();
|
if (!this.challenge) return Promise.reject();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
payload.component = this.challenge.component;
|
payload.component = this.challenge.component;
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
return new FlowsApi(DEFAULT_CONFIG)
|
try {
|
||||||
.flowsExecutorSolve({
|
const challenge = await new FlowsApi(DEFAULT_CONFIG).flowsExecutorSolve({
|
||||||
flowSlug: this.flowSlug || "",
|
flowSlug: this.flowSlug,
|
||||||
query: window.location.search.substring(1),
|
query: window.location.search.substring(1),
|
||||||
flowChallengeResponseRequest: payload,
|
flowChallengeResponseRequest: payload,
|
||||||
})
|
|
||||||
.then((data) => {
|
|
||||||
if (this.inspectorOpen) {
|
|
||||||
window.dispatchEvent(
|
|
||||||
new CustomEvent(EVENT_FLOW_ADVANCE, {
|
|
||||||
bubbles: true,
|
|
||||||
composed: true,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.challenge = data;
|
|
||||||
if (this.challenge.flowInfo) {
|
|
||||||
this.flowInfo = this.challenge.flowInfo;
|
|
||||||
}
|
|
||||||
if (this.challenge.responseErrors) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.catch((e: Error | ResponseError) => {
|
|
||||||
this.errorMessage(e);
|
|
||||||
return false;
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading = false;
|
|
||||||
return false;
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated(): void {
|
|
||||||
configureSentry();
|
|
||||||
this.loading = true;
|
|
||||||
new FlowsApi(DEFAULT_CONFIG)
|
|
||||||
.flowsExecutorGet({
|
|
||||||
flowSlug: this.flowSlug || "",
|
|
||||||
query: window.location.search.substring(1),
|
|
||||||
})
|
|
||||||
.then((challenge) => {
|
|
||||||
if (this.inspectorOpen) {
|
if (this.inspectorOpen) {
|
||||||
window.dispatchEvent(
|
window.dispatchEvent(
|
||||||
new CustomEvent(EVENT_FLOW_ADVANCE, {
|
new CustomEvent(EVENT_FLOW_ADVANCE, {
|
||||||
|
@ -226,14 +210,44 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||||
if (this.challenge.flowInfo) {
|
if (this.challenge.flowInfo) {
|
||||||
this.flowInfo = this.challenge.flowInfo;
|
this.flowInfo = this.challenge.flowInfo;
|
||||||
}
|
}
|
||||||
})
|
if (this.challenge.responseErrors) {
|
||||||
.catch((e: Error | ResponseError) => {
|
return false;
|
||||||
// Catch JSON or Update errors
|
}
|
||||||
this.errorMessage(e);
|
return true;
|
||||||
})
|
} catch (exc: unknown) {
|
||||||
.finally(() => {
|
this.errorMessage(exc as Error | ResponseError);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async firstUpdated(): Promise<void> {
|
||||||
|
configureSentry();
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const challenge = await new FlowsApi(DEFAULT_CONFIG).flowsExecutorGet({
|
||||||
|
flowSlug: this.flowSlug,
|
||||||
|
query: window.location.search.substring(1),
|
||||||
});
|
});
|
||||||
|
if (this.inspectorOpen) {
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent(EVENT_FLOW_ADVANCE, {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.challenge = challenge;
|
||||||
|
if (this.challenge.flowInfo) {
|
||||||
|
this.flowInfo = this.challenge.flowInfo;
|
||||||
|
}
|
||||||
|
} catch (exc: unknown) {
|
||||||
|
// Catch JSON or Update errors
|
||||||
|
this.errorMessage(exc as Error | ResponseError);
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async errorMessage(error: Error | ResponseError): Promise<void> {
|
async errorMessage(error: Error | ResponseError): Promise<void> {
|
||||||
|
@ -412,12 +426,15 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderChallengeWrapper(): TemplateResult {
|
renderChallengeWrapper(): TemplateResult {
|
||||||
|
const logo = html`<div class="pf-c-login__main-header pf-c-brand ak-brand">
|
||||||
|
<img src="${first(this.tenant?.brandingLogo, "")}" alt="authentik Logo" />
|
||||||
|
</div>`;
|
||||||
if (!this.challenge) {
|
if (!this.challenge) {
|
||||||
return html`<ak-empty-state ?loading=${true} header=${msg("Loading")}>
|
return html`${logo}<ak-empty-state ?loading=${true} header=${msg("Loading")}>
|
||||||
</ak-empty-state>`;
|
</ak-empty-state>`;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
${this.loading ? html`<ak-loading-overlay></ak-loading-overlay>` : html``}
|
${this.loading ? html`<ak-loading-overlay></ak-loading-overlay>` : nothing} ${logo}
|
||||||
${until(this.renderChallenge())}
|
${until(this.renderChallenge())}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -453,43 +470,9 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBackgroundOverlay(): TemplateResult {
|
|
||||||
const overlaySVG = html`<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="pf-c-background-image__filter"
|
|
||||||
width="0"
|
|
||||||
height="0"
|
|
||||||
>
|
|
||||||
<filter id="image_overlay">
|
|
||||||
<feColorMatrix
|
|
||||||
in="SourceGraphic"
|
|
||||||
type="matrix"
|
|
||||||
values="1.3 0 0 0 0 0 1.3 0 0 0 0 0 1.3 0 0 0 0 0 1 0"
|
|
||||||
/>
|
|
||||||
<feComponentTransfer color-interpolation-filters="sRGB" result="duotone">
|
|
||||||
<feFuncR
|
|
||||||
type="table"
|
|
||||||
tableValues="0.086274509803922 0.43921568627451"
|
|
||||||
></feFuncR>
|
|
||||||
<feFuncG
|
|
||||||
type="table"
|
|
||||||
tableValues="0.086274509803922 0.43921568627451"
|
|
||||||
></feFuncG>
|
|
||||||
<feFuncB
|
|
||||||
type="table"
|
|
||||||
tableValues="0.086274509803922 0.43921568627451"
|
|
||||||
></feFuncB>
|
|
||||||
<feFuncA type="table" tableValues="0 1"></feFuncA>
|
|
||||||
</feComponentTransfer>
|
|
||||||
</filter>
|
|
||||||
</svg>`;
|
|
||||||
render(overlaySVG, document.body);
|
|
||||||
return overlaySVG;
|
|
||||||
}
|
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
return html` <ak-locale-context>
|
return html` <ak-locale-context>
|
||||||
<div class="pf-c-background-image">${this.renderBackgroundOverlay()}</div>
|
<div class="pf-c-background-image"></div>
|
||||||
<div class="pf-c-page__drawer">
|
<div class="pf-c-page__drawer">
|
||||||
<div class="pf-c-drawer ${this.inspectorOpen ? "pf-m-expanded" : "pf-m-collapsed"}">
|
<div class="pf-c-drawer ${this.inspectorOpen ? "pf-m-expanded" : "pf-m-collapsed"}">
|
||||||
<div class="pf-c-drawer__main">
|
<div class="pf-c-drawer__main">
|
||||||
|
@ -497,14 +480,6 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||||
<div class="pf-c-drawer__body">
|
<div class="pf-c-drawer__body">
|
||||||
<div class="pf-c-login ${this.getLayout()}">
|
<div class="pf-c-login ${this.getLayout()}">
|
||||||
<div class="${this.getLayoutClass()}">
|
<div class="${this.getLayoutClass()}">
|
||||||
<header class="pf-c-login__header">
|
|
||||||
<div class="pf-c-brand ak-brand">
|
|
||||||
<img
|
|
||||||
src="${first(this.tenant?.brandingLogo, "")}"
|
|
||||||
alt="authentik Logo"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<div class="pf-c-login__main">
|
<div class="pf-c-login__main">
|
||||||
${this.renderChallengeWrapper()}
|
${this.renderChallengeWrapper()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -96,7 +96,7 @@ export class LibraryApplication extends AKElement {
|
||||||
this.application.metaPublisher !== "" ||
|
this.application.metaPublisher !== "" ||
|
||||||
this.application.metaDescription !== "";
|
this.application.metaDescription !== "";
|
||||||
|
|
||||||
const classes = { "pf-m-selectable pf-m-selected": this.selected };
|
const classes = { "pf-m-selectable": this.selected, "pf-m-selected": this.selected };
|
||||||
const styles = this.background ? { background: this.background } : {};
|
const styles = this.background ? { background: this.background } : {};
|
||||||
|
|
||||||
return html` <div
|
return html` <div
|
||||||
|
|
|
@ -38,7 +38,9 @@ export class LibraryPageApplicationList extends AKElement {
|
||||||
];
|
];
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
apps: Application[] = [];
|
set apps(value: Application[]) {
|
||||||
|
this.fuse.setCollection(value);
|
||||||
|
}
|
||||||
|
|
||||||
@property()
|
@property()
|
||||||
query = getURLParam<string | undefined>("search", undefined);
|
query = getURLParam<string | undefined>("search", undefined);
|
||||||
|
@ -63,7 +65,7 @@ export class LibraryPageApplicationList extends AKElement {
|
||||||
shouldSort: true,
|
shouldSort: true,
|
||||||
ignoreFieldNorm: true,
|
ignoreFieldNorm: true,
|
||||||
useExtendedSearch: true,
|
useExtendedSearch: true,
|
||||||
threshold: 0.5,
|
threshold: 0.3,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +79,6 @@ export class LibraryPageApplicationList extends AKElement {
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this.fuse.setCollection(this.apps);
|
|
||||||
if (!this.query) {
|
if (!this.query) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5800,12 +5800,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="s848288f8c2265aad">
|
<trans-unit id="s848288f8c2265aad">
|
||||||
<source>Your application has been saved</source>
|
<source>Your application has been saved</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf60f1e5b76897c93">
|
|
||||||
<source>In the Application:</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s7ce65cf482b7bff0">
|
|
||||||
<source>In the Provider:</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s67d858051b34c38b">
|
<trans-unit id="s67d858051b34c38b">
|
||||||
<source>Method's display Name.</source>
|
<source>Method's display Name.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -6075,6 +6069,54 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s52bdc80690a9a8dc">
|
<trans-unit id="s52bdc80690a9a8dc">
|
||||||
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="scda8dc24b561e205">
|
||||||
|
<source>There was an error in the application.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdaca9c2c0361ed3a">
|
||||||
|
<source>Review the application.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb50000a8fada5672">
|
||||||
|
<source>There was an error in the provider.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s21f95eaf151d4ce3">
|
||||||
|
<source>Review the provider.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9fd39a5cb20b4e61">
|
||||||
|
<source>There was an error</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7a6b3453209e1066">
|
||||||
|
<source>There was an error creating the application, but no error message was sent. Please review the server logs.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1a711c19cda48375">
|
||||||
|
<source>Configure LDAP Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9368e965b5c292ab">
|
||||||
|
<source>Configure OAuth2/OpenId Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf5cbccdc6254c8dc">
|
||||||
|
<source>Configure Proxy Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf6d46bb442b77e91">
|
||||||
|
<source>AdditionalScopes</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s2c8c6f89089b31d4">
|
||||||
|
<source>Configure Radius Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfe906cde5dddc041">
|
||||||
|
<source>Configure SAML Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb3defbacd01ad972">
|
||||||
|
<source>Property mappings used for user mapping.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7ccce0ec8d228db6">
|
||||||
|
<source>Configure SCIM Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sd7728d2b6e1d25e9">
|
||||||
|
<source>Property mappings used for group creation.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7513372fe60f6387">
|
||||||
|
<source>Event volume</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -6077,12 +6077,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="s848288f8c2265aad">
|
<trans-unit id="s848288f8c2265aad">
|
||||||
<source>Your application has been saved</source>
|
<source>Your application has been saved</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf60f1e5b76897c93">
|
|
||||||
<source>In the Application:</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s7ce65cf482b7bff0">
|
|
||||||
<source>In the Provider:</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s67d858051b34c38b">
|
<trans-unit id="s67d858051b34c38b">
|
||||||
<source>Method's display Name.</source>
|
<source>Method's display Name.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -6352,6 +6346,54 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s52bdc80690a9a8dc">
|
<trans-unit id="s52bdc80690a9a8dc">
|
||||||
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="scda8dc24b561e205">
|
||||||
|
<source>There was an error in the application.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdaca9c2c0361ed3a">
|
||||||
|
<source>Review the application.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb50000a8fada5672">
|
||||||
|
<source>There was an error in the provider.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s21f95eaf151d4ce3">
|
||||||
|
<source>Review the provider.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9fd39a5cb20b4e61">
|
||||||
|
<source>There was an error</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7a6b3453209e1066">
|
||||||
|
<source>There was an error creating the application, but no error message was sent. Please review the server logs.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1a711c19cda48375">
|
||||||
|
<source>Configure LDAP Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9368e965b5c292ab">
|
||||||
|
<source>Configure OAuth2/OpenId Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf5cbccdc6254c8dc">
|
||||||
|
<source>Configure Proxy Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf6d46bb442b77e91">
|
||||||
|
<source>AdditionalScopes</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s2c8c6f89089b31d4">
|
||||||
|
<source>Configure Radius Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfe906cde5dddc041">
|
||||||
|
<source>Configure SAML Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb3defbacd01ad972">
|
||||||
|
<source>Property mappings used for user mapping.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7ccce0ec8d228db6">
|
||||||
|
<source>Configure SCIM Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sd7728d2b6e1d25e9">
|
||||||
|
<source>Property mappings used for group creation.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7513372fe60f6387">
|
||||||
|
<source>Event volume</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -5716,12 +5716,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="s848288f8c2265aad">
|
<trans-unit id="s848288f8c2265aad">
|
||||||
<source>Your application has been saved</source>
|
<source>Your application has been saved</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf60f1e5b76897c93">
|
|
||||||
<source>In the Application:</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s7ce65cf482b7bff0">
|
|
||||||
<source>In the Provider:</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s67d858051b34c38b">
|
<trans-unit id="s67d858051b34c38b">
|
||||||
<source>Method's display Name.</source>
|
<source>Method's display Name.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -5991,6 +5985,54 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s52bdc80690a9a8dc">
|
<trans-unit id="s52bdc80690a9a8dc">
|
||||||
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="scda8dc24b561e205">
|
||||||
|
<source>There was an error in the application.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdaca9c2c0361ed3a">
|
||||||
|
<source>Review the application.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb50000a8fada5672">
|
||||||
|
<source>There was an error in the provider.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s21f95eaf151d4ce3">
|
||||||
|
<source>Review the provider.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9fd39a5cb20b4e61">
|
||||||
|
<source>There was an error</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7a6b3453209e1066">
|
||||||
|
<source>There was an error creating the application, but no error message was sent. Please review the server logs.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1a711c19cda48375">
|
||||||
|
<source>Configure LDAP Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9368e965b5c292ab">
|
||||||
|
<source>Configure OAuth2/OpenId Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf5cbccdc6254c8dc">
|
||||||
|
<source>Configure Proxy Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf6d46bb442b77e91">
|
||||||
|
<source>AdditionalScopes</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s2c8c6f89089b31d4">
|
||||||
|
<source>Configure Radius Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfe906cde5dddc041">
|
||||||
|
<source>Configure SAML Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb3defbacd01ad972">
|
||||||
|
<source>Property mappings used for user mapping.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7ccce0ec8d228db6">
|
||||||
|
<source>Configure SCIM Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sd7728d2b6e1d25e9">
|
||||||
|
<source>Property mappings used for group creation.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7513372fe60f6387">
|
||||||
|
<source>Event volume</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -7617,14 +7617,6 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||||
<source>Your application has been saved</source>
|
<source>Your application has been saved</source>
|
||||||
<target>L'application a été sauvegardée</target>
|
<target>L'application a été sauvegardée</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf60f1e5b76897c93">
|
|
||||||
<source>In the Application:</source>
|
|
||||||
<target>Dans l'application :</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s7ce65cf482b7bff0">
|
|
||||||
<source>In the Provider:</source>
|
|
||||||
<target>Dans le fournisseur :</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s67d858051b34c38b">
|
<trans-unit id="s67d858051b34c38b">
|
||||||
<source>Method's display Name.</source>
|
<source>Method's display Name.</source>
|
||||||
<target>Nom d'affichage de la méthode.</target>
|
<target>Nom d'affichage de la méthode.</target>
|
||||||
|
@ -7986,6 +7978,54 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||||
<trans-unit id="s52bdc80690a9a8dc">
|
<trans-unit id="s52bdc80690a9a8dc">
|
||||||
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
||||||
<target>Lorsqu'activé, l'étape acceptera toujours l'identifiant utilisateur donné et continuera.</target>
|
<target>Lorsqu'activé, l'étape acceptera toujours l'identifiant utilisateur donné et continuera.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="scda8dc24b561e205">
|
||||||
|
<source>There was an error in the application.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdaca9c2c0361ed3a">
|
||||||
|
<source>Review the application.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb50000a8fada5672">
|
||||||
|
<source>There was an error in the provider.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s21f95eaf151d4ce3">
|
||||||
|
<source>Review the provider.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9fd39a5cb20b4e61">
|
||||||
|
<source>There was an error</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7a6b3453209e1066">
|
||||||
|
<source>There was an error creating the application, but no error message was sent. Please review the server logs.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1a711c19cda48375">
|
||||||
|
<source>Configure LDAP Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9368e965b5c292ab">
|
||||||
|
<source>Configure OAuth2/OpenId Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf5cbccdc6254c8dc">
|
||||||
|
<source>Configure Proxy Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf6d46bb442b77e91">
|
||||||
|
<source>AdditionalScopes</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s2c8c6f89089b31d4">
|
||||||
|
<source>Configure Radius Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfe906cde5dddc041">
|
||||||
|
<source>Configure SAML Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb3defbacd01ad972">
|
||||||
|
<source>Property mappings used for user mapping.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7ccce0ec8d228db6">
|
||||||
|
<source>Configure SCIM Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sd7728d2b6e1d25e9">
|
||||||
|
<source>Property mappings used for group creation.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7513372fe60f6387">
|
||||||
|
<source>Event volume</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -5924,12 +5924,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="s848288f8c2265aad">
|
<trans-unit id="s848288f8c2265aad">
|
||||||
<source>Your application has been saved</source>
|
<source>Your application has been saved</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf60f1e5b76897c93">
|
|
||||||
<source>In the Application:</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s7ce65cf482b7bff0">
|
|
||||||
<source>In the Provider:</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s67d858051b34c38b">
|
<trans-unit id="s67d858051b34c38b">
|
||||||
<source>Method's display Name.</source>
|
<source>Method's display Name.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -6199,6 +6193,54 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s52bdc80690a9a8dc">
|
<trans-unit id="s52bdc80690a9a8dc">
|
||||||
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="scda8dc24b561e205">
|
||||||
|
<source>There was an error in the application.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdaca9c2c0361ed3a">
|
||||||
|
<source>Review the application.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb50000a8fada5672">
|
||||||
|
<source>There was an error in the provider.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s21f95eaf151d4ce3">
|
||||||
|
<source>Review the provider.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9fd39a5cb20b4e61">
|
||||||
|
<source>There was an error</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7a6b3453209e1066">
|
||||||
|
<source>There was an error creating the application, but no error message was sent. Please review the server logs.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1a711c19cda48375">
|
||||||
|
<source>Configure LDAP Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9368e965b5c292ab">
|
||||||
|
<source>Configure OAuth2/OpenId Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf5cbccdc6254c8dc">
|
||||||
|
<source>Configure Proxy Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf6d46bb442b77e91">
|
||||||
|
<source>AdditionalScopes</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s2c8c6f89089b31d4">
|
||||||
|
<source>Configure Radius Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfe906cde5dddc041">
|
||||||
|
<source>Configure SAML Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb3defbacd01ad972">
|
||||||
|
<source>Property mappings used for user mapping.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7ccce0ec8d228db6">
|
||||||
|
<source>Configure SCIM Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sd7728d2b6e1d25e9">
|
||||||
|
<source>Property mappings used for group creation.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7513372fe60f6387">
|
||||||
|
<source>Event volume</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -7556,14 +7556,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<source>Your application has been saved</source>
|
<source>Your application has been saved</source>
|
||||||
<target>Ŷōũŕ àƥƥĺĩćàţĩōń ĥàś ƀēēń śàvēď</target>
|
<target>Ŷōũŕ àƥƥĺĩćàţĩōń ĥàś ƀēēń śàvēď</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf60f1e5b76897c93">
|
|
||||||
<source>In the Application:</source>
|
|
||||||
<target>Ĩń ţĥē Àƥƥĺĩćàţĩōń:</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s7ce65cf482b7bff0">
|
|
||||||
<source>In the Provider:</source>
|
|
||||||
<target>Ĩń ţĥē Ƥŕōvĩďēŕ:</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s67d858051b34c38b">
|
<trans-unit id="s67d858051b34c38b">
|
||||||
<source>Method's display Name.</source>
|
<source>Method's display Name.</source>
|
||||||
<target>Mēţĥōď'ś ďĩśƥĺàŷ Ńàmē.</target>
|
<target>Mēţĥōď'ś ďĩśƥĺàŷ Ńàmē.</target>
|
||||||
|
@ -7732,149 +7724,261 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s4bd386db7302bb22">
|
<trans-unit id="s4bd386db7302bb22">
|
||||||
<source>Create With Wizard</source>
|
<source>Create With Wizard</source>
|
||||||
|
<target>Ćŕēàţē Ŵĩţĥ Ŵĩźàŕď</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s070fdfb03034ca9b">
|
<trans-unit id="s070fdfb03034ca9b">
|
||||||
<source>One hint, 'New Application Wizard', is currently hidden</source>
|
<source>One hint, 'New Application Wizard', is currently hidden</source>
|
||||||
|
<target>Ōńē ĥĩńţ, 'Ńēŵ Àƥƥĺĩćàţĩōń Ŵĩźàŕď', ĩś ćũŕŕēńţĺŷ ĥĩďďēń</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s61bd841e66966325">
|
<trans-unit id="s61bd841e66966325">
|
||||||
<source>External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
<source>External applications that use authentik as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.</source>
|
||||||
|
<target>Ēxţēŕńàĺ àƥƥĺĩćàţĩōńś ţĥàţ ũśē àũţĥēńţĩķ àś àń ĩďēńţĩţŷ ƥŕōvĩďēŕ vĩà ƥŕōţōćōĺś ĺĩķē ŌÀũţĥ2 àńď ŚÀMĹ. Àĺĺ àƥƥĺĩćàţĩōńś àŕē śĥōŵń ĥēŕē, ēvēń ōńēś ŷōũ ćàńńōţ àććēśś.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s1cc306d8e28c4464">
|
<trans-unit id="s1cc306d8e28c4464">
|
||||||
<source>Deny message</source>
|
<source>Deny message</source>
|
||||||
|
<target>Ďēńŷ mēśśàĝē</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s6985c401e1100122">
|
<trans-unit id="s6985c401e1100122">
|
||||||
<source>Message shown when this stage is run.</source>
|
<source>Message shown when this stage is run.</source>
|
||||||
|
<target>Mēśśàĝē śĥōŵń ŵĥēń ţĥĩś śţàĝē ĩś ŕũń.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s09f0c100d0ad2fec">
|
<trans-unit id="s09f0c100d0ad2fec">
|
||||||
<source>Open Wizard</source>
|
<source>Open Wizard</source>
|
||||||
|
<target>Ōƥēń Ŵĩźàŕď</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf2ef885f7d0a101d">
|
<trans-unit id="sf2ef885f7d0a101d">
|
||||||
<source>Demo Wizard</source>
|
<source>Demo Wizard</source>
|
||||||
|
<target>Ďēmō Ŵĩźàŕď</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s77505ee5d2e45e53">
|
<trans-unit id="s77505ee5d2e45e53">
|
||||||
<source>Run the demo wizard</source>
|
<source>Run the demo wizard</source>
|
||||||
|
<target>Ŕũń ţĥē ďēmō ŵĩźàŕď</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s4498e890d47a8066">
|
<trans-unit id="s4498e890d47a8066">
|
||||||
<source>OAuth2/OIDC (Open Authorization/OpenID Connect)</source>
|
<source>OAuth2/OIDC (Open Authorization/OpenID Connect)</source>
|
||||||
|
<target>ŌÀũţĥ2/ŌĨĎĆ (Ōƥēń Àũţĥōŕĩźàţĩōń/ŌƥēńĨĎ Ćōńńēćţ)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s4f2e195d09e2868c">
|
<trans-unit id="s4f2e195d09e2868c">
|
||||||
<source>LDAP (Lightweight Directory Access Protocol)</source>
|
<source>LDAP (Lightweight Directory Access Protocol)</source>
|
||||||
|
<target>ĹĎÀƤ (Ĺĩĝĥţŵēĩĝĥţ Ďĩŕēćţōŕŷ Àććēśś Ƥŕōţōćōĺ)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s7f5bb0c9923315ed">
|
<trans-unit id="s7f5bb0c9923315ed">
|
||||||
<source>Forward Auth (Single Application)</source>
|
<source>Forward Auth (Single Application)</source>
|
||||||
|
<target>Ƒōŕŵàŕď Àũţĥ (Śĩńĝĺē Àƥƥĺĩćàţĩōń)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf8008d2d6b064b95">
|
<trans-unit id="sf8008d2d6b064b95">
|
||||||
<source>Forward Auth (Domain Level)</source>
|
<source>Forward Auth (Domain Level)</source>
|
||||||
|
<target>Ƒōŕŵàŕď Àũţĥ (Ďōmàĩń Ĺēvēĺ)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sfa8a1ffa9fee07d3">
|
<trans-unit id="sfa8a1ffa9fee07d3">
|
||||||
<source>SAML (Security Assertion Markup Language)</source>
|
<source>SAML (Security Assertion Markup Language)</source>
|
||||||
|
<target>ŚÀMĹ (Śēćũŕĩţŷ Àśśēŕţĩōń Màŕķũƥ Ĺàńĝũàĝē)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s848a23972e388662">
|
<trans-unit id="s848a23972e388662">
|
||||||
<source>RADIUS (Remote Authentication Dial-In User Service)</source>
|
<source>RADIUS (Remote Authentication Dial-In User Service)</source>
|
||||||
|
<target>ŔÀĎĨŨŚ (Ŕēmōţē Àũţĥēńţĩćàţĩōń Ďĩàĺ-Ĩń Ũśēŕ Śēŕvĩćē)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s3e902999ddf7b50e">
|
<trans-unit id="s3e902999ddf7b50e">
|
||||||
<source>SCIM (System for Cross-domain Identity Management)</source>
|
<source>SCIM (System for Cross-domain Identity Management)</source>
|
||||||
|
<target>ŚĆĨM (Śŷśţēm ƒōŕ Ćŕōśś-ďōmàĩń Ĩďēńţĩţŷ Màńàĝēmēńţ)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sdc5690be4a342985">
|
<trans-unit id="sdc5690be4a342985">
|
||||||
<source>The token has been copied to your clipboard</source>
|
<source>The token has been copied to your clipboard</source>
|
||||||
|
<target>Ţĥē ţōķēń ĥàś ƀēēń ćōƥĩēď ţō ŷōũŕ ćĺĩƥƀōàŕď</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s7f3edfee24690c9f">
|
<trans-unit id="s7f3edfee24690c9f">
|
||||||
<source>The token was displayed because authentik does not have permission to write to the clipboard</source>
|
<source>The token was displayed because authentik does not have permission to write to the clipboard</source>
|
||||||
|
<target>Ţĥē ţōķēń ŵàś ďĩśƥĺàŷēď ƀēćàũśē àũţĥēńţĩķ ďōēś ńōţ ĥàvē ƥēŕmĩśśĩōń ţō ŵŕĩţē ţō ţĥē ćĺĩƥƀōàŕď</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="saf6097bfa25205b8">
|
<trans-unit id="saf6097bfa25205b8">
|
||||||
<source>A copy of this recovery link has been placed in your clipboard</source>
|
<source>A copy of this recovery link has been placed in your clipboard</source>
|
||||||
|
<target>À ćōƥŷ ōƒ ţĥĩś ŕēćōvēŕŷ ĺĩńķ ĥàś ƀēēń ƥĺàćēď ĩń ŷōũŕ ćĺĩƥƀōàŕď</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s5b8ee296ed258568">
|
<trans-unit id="s5b8ee296ed258568">
|
||||||
<source>The current tenant must have a recovery flow configured to use a recovery link</source>
|
<source>The current tenant must have a recovery flow configured to use a recovery link</source>
|
||||||
|
<target>Ţĥē ćũŕŕēńţ ţēńàńţ mũśţ ĥàvē à ŕēćōvēŕŷ ƒĺōŵ ćōńƒĩĝũŕēď ţō ũśē à ŕēćōvēŕŷ ĺĩńķ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s895514dda9cb9c94">
|
<trans-unit id="s895514dda9cb9c94">
|
||||||
<source>Create recovery link</source>
|
<source>Create recovery link</source>
|
||||||
|
<target>Ćŕēàţē ŕēćōvēŕŷ ĺĩńķ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="se5c795faf2c07514">
|
<trans-unit id="se5c795faf2c07514">
|
||||||
<source>Create Recovery Link</source>
|
<source>Create Recovery Link</source>
|
||||||
|
<target>Ćŕēàţē Ŕēćōvēŕŷ Ĺĩńķ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s84fcddede27b8e2a">
|
<trans-unit id="s84fcddede27b8e2a">
|
||||||
<source>External</source>
|
<source>External</source>
|
||||||
|
<target>Ēxţēŕńàĺ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s1a635369edaf4dc3">
|
<trans-unit id="s1a635369edaf4dc3">
|
||||||
<source>Service account</source>
|
<source>Service account</source>
|
||||||
|
<target>Śēŕvĩćē àććōũńţ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sff930bf2834e2201">
|
<trans-unit id="sff930bf2834e2201">
|
||||||
<source>Service account (internal)</source>
|
<source>Service account (internal)</source>
|
||||||
|
<target>Śēŕvĩćē àććōũńţ (ĩńţēŕńàĺ)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s66313b45b69cfc88">
|
<trans-unit id="s66313b45b69cfc88">
|
||||||
<source>Check the release notes</source>
|
<source>Check the release notes</source>
|
||||||
|
<target>Ćĥēćķ ţĥē ŕēĺēàśē ńōţēś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sb4d7bae2440d9781">
|
<trans-unit id="sb4d7bae2440d9781">
|
||||||
<source>User Statistics</source>
|
<source>User Statistics</source>
|
||||||
|
<target>Ũśēŕ Śţàţĩśţĩćś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s0924f51b028233a3">
|
<trans-unit id="s0924f51b028233a3">
|
||||||
<source><No name set></source>
|
<source><No name set></source>
|
||||||
|
<target><Ńō ńàmē śēţ></target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sdc9a6ad1af30572c">
|
<trans-unit id="sdc9a6ad1af30572c">
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
<source>For nginx's auth_request or traefik's forwardAuth</source>
|
||||||
|
<target>Ƒōŕ ńĝĩńx'ś àũţĥ_ŕēǫũēśţ ōŕ ţŕàēƒĩķ'ś ƒōŕŵàŕďÀũţĥ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sfc31264ef7ff86ef">
|
<trans-unit id="sfc31264ef7ff86ef">
|
||||||
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
<source>For nginx's auth_request or traefik's forwardAuth per root domain</source>
|
||||||
|
<target>Ƒōŕ ńĝĩńx'ś àũţĥ_ŕēǫũēśţ ōŕ ţŕàēƒĩķ'ś ƒōŕŵàŕďÀũţĥ ƥēŕ ŕōōţ ďōmàĩń</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc615309d10a9228c">
|
<trans-unit id="sc615309d10a9228c">
|
||||||
<source>RBAC is in preview.</source>
|
<source>RBAC is in preview.</source>
|
||||||
|
<target>ŔßÀĆ ĩś ĩń ƥŕēvĩēŵ.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s32babfed740fd3c1">
|
<trans-unit id="s32babfed740fd3c1">
|
||||||
<source>User type used for newly created users.</source>
|
<source>User type used for newly created users.</source>
|
||||||
|
<target>Ũśēŕ ţŷƥē ũśēď ƒōŕ ńēŵĺŷ ćŕēàţēď ũśēŕś.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s4a34a6be4c68ec87">
|
<trans-unit id="s4a34a6be4c68ec87">
|
||||||
<source>Users created</source>
|
<source>Users created</source>
|
||||||
|
<target>Ũśēŕś ćŕēàţēď</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s275c956687e2e656">
|
<trans-unit id="s275c956687e2e656">
|
||||||
<source>Failed logins</source>
|
<source>Failed logins</source>
|
||||||
|
<target>Ƒàĩĺēď ĺōĝĩńś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sb35c08e3a541188f">
|
<trans-unit id="sb35c08e3a541188f">
|
||||||
<source>Also known as Client ID.</source>
|
<source>Also known as Client ID.</source>
|
||||||
|
<target>Àĺśō ķńōŵń àś Ćĺĩēńţ ĨĎ.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sd46fd9b647cfea10">
|
<trans-unit id="sd46fd9b647cfea10">
|
||||||
<source>Also known as Client Secret.</source>
|
<source>Also known as Client Secret.</source>
|
||||||
|
<target>Àĺśō ķńōŵń àś Ćĺĩēńţ Śēćŕēţ.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s4476e9c50cfd13f4">
|
<trans-unit id="s4476e9c50cfd13f4">
|
||||||
<source>Global status</source>
|
<source>Global status</source>
|
||||||
|
<target>Ĝĺōƀàĺ śţàţũś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sd21a971eea208533">
|
<trans-unit id="sd21a971eea208533">
|
||||||
<source>Vendor</source>
|
<source>Vendor</source>
|
||||||
|
<target>Vēńďōŕ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sadadfe9dfa06d7dd">
|
<trans-unit id="sadadfe9dfa06d7dd">
|
||||||
<source>No sync status.</source>
|
<source>No sync status.</source>
|
||||||
|
<target>Ńō śŷńć śţàţũś.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s2b1c81130a65a55b">
|
<trans-unit id="s2b1c81130a65a55b">
|
||||||
<source>Sync currently running.</source>
|
<source>Sync currently running.</source>
|
||||||
|
<target>Śŷńć ćũŕŕēńţĺŷ ŕũńńĩńĝ.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf36170f71cea38c2">
|
<trans-unit id="sf36170f71cea38c2">
|
||||||
<source>Connectivity</source>
|
<source>Connectivity</source>
|
||||||
|
<target>Ćōńńēćţĩvĩţŷ</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sd94e99af8b41ff54">
|
<trans-unit id="sd94e99af8b41ff54">
|
||||||
<source>0: Too guessable: risky password. (guesses &lt; 10^3)</source>
|
<source>0: Too guessable: risky password. (guesses &lt; 10^3)</source>
|
||||||
|
<target>0: Ţōō ĝũēśśàƀĺē: ŕĩśķŷ ƥàśśŵōŕď. (ĝũēśśēś &ĺţ; 10^3)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc926385d1a624c3a">
|
<trans-unit id="sc926385d1a624c3a">
|
||||||
<source>1: Very guessable: protection from throttled online attacks. (guesses &lt; 10^6)</source>
|
<source>1: Very guessable: protection from throttled online attacks. (guesses &lt; 10^6)</source>
|
||||||
|
<target>1: Vēŕŷ ĝũēśśàƀĺē: ƥŕōţēćţĩōń ƒŕōm ţĥŕōţţĺēď ōńĺĩńē àţţàćķś. (ĝũēśśēś &ĺţ; 10^6)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s8aae61c41319602c">
|
<trans-unit id="s8aae61c41319602c">
|
||||||
<source>2: Somewhat guessable: protection from unthrottled online attacks. (guesses &lt; 10^8)</source>
|
<source>2: Somewhat guessable: protection from unthrottled online attacks. (guesses &lt; 10^8)</source>
|
||||||
|
<target>2: Śōmēŵĥàţ ĝũēśśàƀĺē: ƥŕōţēćţĩōń ƒŕōm ũńţĥŕōţţĺēď ōńĺĩńē àţţàćķś. (ĝũēśśēś &ĺţ; 10^8)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc1f4b57e722a89d6">
|
<trans-unit id="sc1f4b57e722a89d6">
|
||||||
<source>3: Safely unguessable: moderate protection from offline slow-hash scenario. (guesses &lt; 10^10)</source>
|
<source>3: Safely unguessable: moderate protection from offline slow-hash scenario. (guesses &lt; 10^10)</source>
|
||||||
|
<target>3: Śàƒēĺŷ ũńĝũēśśàƀĺē: mōďēŕàţē ƥŕōţēćţĩōń ƒŕōm ōƒƒĺĩńē śĺōŵ-ĥàśĥ śćēńàŕĩō. (ĝũēśśēś &ĺţ; 10^10)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sd47f3d3c9741343d">
|
<trans-unit id="sd47f3d3c9741343d">
|
||||||
<source>4: Very unguessable: strong protection from offline slow-hash scenario. (guesses &gt;= 10^10)</source>
|
<source>4: Very unguessable: strong protection from offline slow-hash scenario. (guesses &gt;= 10^10)</source>
|
||||||
|
<target>4: Vēŕŷ ũńĝũēśśàƀĺē: śţŕōńĝ ƥŕōţēćţĩōń ƒŕōm ōƒƒĺĩńē śĺōŵ-ĥàśĥ śćēńàŕĩō. (ĝũēśśēś &ĝţ;= 10^10)</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s3d2a8b86a4f5a810">
|
<trans-unit id="s3d2a8b86a4f5a810">
|
||||||
<source>Successfully created user and added to group <x id="0" equiv-text="${this.group.name}"/></source>
|
<source>Successfully created user and added to group <x id="0" equiv-text="${this.group.name}"/></source>
|
||||||
|
<target>Śũććēśśƒũĺĺŷ ćŕēàţēď ũśēŕ àńď àďďēď ţō ĝŕōũƥ <x id="0" equiv-text="${this.group.name}"/></target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s824e0943a7104668">
|
<trans-unit id="s824e0943a7104668">
|
||||||
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
|
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
|
||||||
|
<target>Ţĥĩś ũśēŕ ŵĩĺĺ ƀē àďďēď ţō ţĥē ĝŕōũƥ "<x id="0" equiv-text="${this.targetGroup.name}"/>".</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s62e7f6ed7d9cb3ca">
|
<trans-unit id="s62e7f6ed7d9cb3ca">
|
||||||
<source>Pretend user exists</source>
|
<source>Pretend user exists</source>
|
||||||
|
<target>Ƥŕēţēńď ũśēŕ ēxĩśţś</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s52bdc80690a9a8dc">
|
<trans-unit id="s52bdc80690a9a8dc">
|
||||||
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
||||||
|
<target>Ŵĥēń ēńàƀĺēď, ţĥē śţàĝē ŵĩĺĺ àĺŵàŷś àććēƥţ ţĥē ĝĩvēń ũśēŕ ĩďēńţĩƒĩēŕ àńď ćōńţĩńũē.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="scda8dc24b561e205">
|
||||||
|
<source>There was an error in the application.</source>
|
||||||
|
<target>Ţĥēŕē ŵàś àń ēŕŕōŕ ĩń ţĥē àƥƥĺĩćàţĩōń.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdaca9c2c0361ed3a">
|
||||||
|
<source>Review the application.</source>
|
||||||
|
<target>Ŕēvĩēŵ ţĥē àƥƥĺĩćàţĩōń.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb50000a8fada5672">
|
||||||
|
<source>There was an error in the provider.</source>
|
||||||
|
<target>Ţĥēŕē ŵàś àń ēŕŕōŕ ĩń ţĥē ƥŕōvĩďēŕ.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s21f95eaf151d4ce3">
|
||||||
|
<source>Review the provider.</source>
|
||||||
|
<target>Ŕēvĩēŵ ţĥē ƥŕōvĩďēŕ.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9fd39a5cb20b4e61">
|
||||||
|
<source>There was an error</source>
|
||||||
|
<target>Ţĥēŕē ŵàś àń ēŕŕōŕ</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7a6b3453209e1066">
|
||||||
|
<source>There was an error creating the application, but no error message was sent. Please review the server logs.</source>
|
||||||
|
<target>Ţĥēŕē ŵàś àń ēŕŕōŕ ćŕēàţĩńĝ ţĥē àƥƥĺĩćàţĩōń, ƀũţ ńō ēŕŕōŕ mēśśàĝē ŵàś śēńţ. Ƥĺēàśē ŕēvĩēŵ ţĥē śēŕvēŕ ĺōĝś.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1a711c19cda48375">
|
||||||
|
<source>Configure LDAP Provider</source>
|
||||||
|
<target>Ćōńƒĩĝũŕē ĹĎÀƤ Ƥŕōvĩďēŕ</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9368e965b5c292ab">
|
||||||
|
<source>Configure OAuth2/OpenId Provider</source>
|
||||||
|
<target>Ćōńƒĩĝũŕē ŌÀũţĥ2/ŌƥēńĨď Ƥŕōvĩďēŕ</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf5cbccdc6254c8dc">
|
||||||
|
<source>Configure Proxy Provider</source>
|
||||||
|
<target>Ćōńƒĩĝũŕē Ƥŕōxŷ Ƥŕōvĩďēŕ</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf6d46bb442b77e91">
|
||||||
|
<source>AdditionalScopes</source>
|
||||||
|
<target>ÀďďĩţĩōńàĺŚćōƥēś</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s2c8c6f89089b31d4">
|
||||||
|
<source>Configure Radius Provider</source>
|
||||||
|
<target>Ćōńƒĩĝũŕē Ŕàďĩũś Ƥŕōvĩďēŕ</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfe906cde5dddc041">
|
||||||
|
<source>Configure SAML Provider</source>
|
||||||
|
<target>Ćōńƒĩĝũŕē ŚÀMĹ Ƥŕōvĩďēŕ</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb3defbacd01ad972">
|
||||||
|
<source>Property mappings used for user mapping.</source>
|
||||||
|
<target>Ƥŕōƥēŕţŷ màƥƥĩńĝś ũśēď ƒōŕ ũśēŕ màƥƥĩńĝ.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7ccce0ec8d228db6">
|
||||||
|
<source>Configure SCIM Provider</source>
|
||||||
|
<target>Ćōńƒĩĝũŕē ŚĆĨM Ƥŕōvĩďēŕ</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sd7728d2b6e1d25e9">
|
||||||
|
<source>Property mappings used for group creation.</source>
|
||||||
|
<target>Ƥŕōƥēŕţŷ màƥƥĩńĝś ũśēď ƒōŕ ĝŕōũƥ ćŕēàţĩōń.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7513372fe60f6387">
|
||||||
|
<source>Event volume</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body></file></xliff>
|
</body></file></xliff>
|
||||||
|
|
|
@ -5709,12 +5709,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="s848288f8c2265aad">
|
<trans-unit id="s848288f8c2265aad">
|
||||||
<source>Your application has been saved</source>
|
<source>Your application has been saved</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf60f1e5b76897c93">
|
|
||||||
<source>In the Application:</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s7ce65cf482b7bff0">
|
|
||||||
<source>In the Provider:</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s67d858051b34c38b">
|
<trans-unit id="s67d858051b34c38b">
|
||||||
<source>Method's display Name.</source>
|
<source>Method's display Name.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -5984,6 +5978,54 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s52bdc80690a9a8dc">
|
<trans-unit id="s52bdc80690a9a8dc">
|
||||||
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="scda8dc24b561e205">
|
||||||
|
<source>There was an error in the application.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdaca9c2c0361ed3a">
|
||||||
|
<source>Review the application.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb50000a8fada5672">
|
||||||
|
<source>There was an error in the provider.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s21f95eaf151d4ce3">
|
||||||
|
<source>Review the provider.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9fd39a5cb20b4e61">
|
||||||
|
<source>There was an error</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7a6b3453209e1066">
|
||||||
|
<source>There was an error creating the application, but no error message was sent. Please review the server logs.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1a711c19cda48375">
|
||||||
|
<source>Configure LDAP Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9368e965b5c292ab">
|
||||||
|
<source>Configure OAuth2/OpenId Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf5cbccdc6254c8dc">
|
||||||
|
<source>Configure Proxy Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf6d46bb442b77e91">
|
||||||
|
<source>AdditionalScopes</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s2c8c6f89089b31d4">
|
||||||
|
<source>Configure Radius Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfe906cde5dddc041">
|
||||||
|
<source>Configure SAML Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb3defbacd01ad972">
|
||||||
|
<source>Property mappings used for user mapping.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7ccce0ec8d228db6">
|
||||||
|
<source>Configure SCIM Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sd7728d2b6e1d25e9">
|
||||||
|
<source>Property mappings used for group creation.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7513372fe60f6387">
|
||||||
|
<source>Event volume</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0"?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
<?xml version="1.0" ?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||||
<file target-language="zh-Hans" source-language="en" original="lit-localize-inputs" datatype="plaintext">
|
<file target-language="zh-Hans" source-language="en" original="lit-localize-inputs" datatype="plaintext">
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="s4caed5b7a7e5d89b">
|
<trans-unit id="s4caed5b7a7e5d89b">
|
||||||
|
@ -613,9 +613,9 @@
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="saa0e2675da69651b">
|
<trans-unit id="saa0e2675da69651b">
|
||||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||||
<target>未找到 URL "
|
<target>未找到 URL "
|
||||||
<x id="0" equiv-text="${this.url}"/>"。</target>
|
<x id="0" equiv-text="${this.url}"/>"。</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s58cd9c2fe836d9c6">
|
<trans-unit id="s58cd9c2fe836d9c6">
|
||||||
|
@ -1057,8 +1057,8 @@
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sa8384c9c26731f83">
|
<trans-unit id="sa8384c9c26731f83">
|
||||||
<source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source>
|
<source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source>
|
||||||
<target>要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
|
<target>要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s55787f4dfcdce52b">
|
<trans-unit id="s55787f4dfcdce52b">
|
||||||
|
@ -1799,8 +1799,8 @@
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sa90b7809586c35ce">
|
<trans-unit id="sa90b7809586c35ce">
|
||||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||||
<target>输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target>
|
<target>输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s0410779cb47de312">
|
<trans-unit id="s0410779cb47de312">
|
||||||
|
@ -2988,8 +2988,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s76768bebabb7d543">
|
<trans-unit id="s76768bebabb7d543">
|
||||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
|
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
|
||||||
<target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
|
<target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s026555347e589f0e">
|
<trans-unit id="s026555347e589f0e">
|
||||||
|
@ -3781,8 +3781,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s7b1fba26d245cb1c">
|
<trans-unit id="s7b1fba26d245cb1c">
|
||||||
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
|
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
|
||||||
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。</target>
|
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s44536d20bb5c8257">
|
<trans-unit id="s44536d20bb5c8257">
|
||||||
|
@ -3791,8 +3791,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s3bb51cabb02b997e">
|
<trans-unit id="s3bb51cabb02b997e">
|
||||||
<source>Format: "weeks=3;days=2;hours=3,seconds=2".</source>
|
<source>Format: "weeks=3;days=2;hours=3,seconds=2".</source>
|
||||||
<target>格式:"weeks=3;days=2;hours=3,seconds=2"。</target>
|
<target>格式:"weeks=3;days=2;hours=3,seconds=2"。</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s04bfd02201db5ab8">
|
<trans-unit id="s04bfd02201db5ab8">
|
||||||
|
@ -3988,10 +3988,10 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sa95a538bfbb86111">
|
<trans-unit id="sa95a538bfbb86111">
|
||||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||||
<target>您确定要更新
|
<target>您确定要更新
|
||||||
<x id="0" equiv-text="${this.objectLabel}"/>"
|
<x id="0" equiv-text="${this.objectLabel}"/>"
|
||||||
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
|
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc92d7cfb6ee1fec6">
|
<trans-unit id="sc92d7cfb6ee1fec6">
|
||||||
|
@ -5077,7 +5077,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sdf1d8edef27236f0">
|
<trans-unit id="sdf1d8edef27236f0">
|
||||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||||
<target>像 YubiKey 这样的“漫游”身份验证器</target>
|
<target>像 YubiKey 这样的“漫游”身份验证器</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -5412,10 +5412,10 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s2d5f69929bb7221d">
|
<trans-unit id="s2d5f69929bb7221d">
|
||||||
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
|
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
|
||||||
<target>
|
<target>
|
||||||
<x id="0" equiv-text="${prompt.name}"/>("
|
<x id="0" equiv-text="${prompt.name}"/>("
|
||||||
<x id="1" equiv-text="${prompt.fieldKey}"/>",类型为
|
<x id="1" equiv-text="${prompt.fieldKey}"/>",类型为
|
||||||
<x id="2" equiv-text="${prompt.type}"/>)</target>
|
<x id="2" equiv-text="${prompt.type}"/>)</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -5464,7 +5464,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s1608b2f94fa0dbd4">
|
<trans-unit id="s1608b2f94fa0dbd4">
|
||||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||||
<target>如果设置时长大于 0,用户可以选择“保持登录”选项,这将使用户的会话延长此处设置的时间。</target>
|
<target>如果设置时长大于 0,用户可以选择“保持登录”选项,这将使用户的会话延长此处设置的时间。</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -7619,14 +7619,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<source>Your application has been saved</source>
|
<source>Your application has been saved</source>
|
||||||
<target>您的应用程序已保存</target>
|
<target>您的应用程序已保存</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf60f1e5b76897c93">
|
|
||||||
<source>In the Application:</source>
|
|
||||||
<target>在应用程序中:</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s7ce65cf482b7bff0">
|
|
||||||
<source>In the Provider:</source>
|
|
||||||
<target>在提供程序中:</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s67d858051b34c38b">
|
<trans-unit id="s67d858051b34c38b">
|
||||||
<source>Method's display Name.</source>
|
<source>Method's display Name.</source>
|
||||||
<target>方法的显示名称。</target>
|
<target>方法的显示名称。</target>
|
||||||
|
@ -7978,7 +7970,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<target>成功创建用户并添加到组 <x id="0" equiv-text="${this.group.name}"/></target>
|
<target>成功创建用户并添加到组 <x id="0" equiv-text="${this.group.name}"/></target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s824e0943a7104668">
|
<trans-unit id="s824e0943a7104668">
|
||||||
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
|
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
|
||||||
<target>此用户将会被添加到组 &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;。</target>
|
<target>此用户将会被添加到组 &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;。</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s62e7f6ed7d9cb3ca">
|
<trans-unit id="s62e7f6ed7d9cb3ca">
|
||||||
|
@ -7988,6 +7980,70 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="s52bdc80690a9a8dc">
|
<trans-unit id="s52bdc80690a9a8dc">
|
||||||
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
||||||
<target>启用时,此阶段总是会接受指定的用户 ID 并继续。</target>
|
<target>启用时,此阶段总是会接受指定的用户 ID 并继续。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="scda8dc24b561e205">
|
||||||
|
<source>There was an error in the application.</source>
|
||||||
|
<target>应用程序中存在一个错误。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdaca9c2c0361ed3a">
|
||||||
|
<source>Review the application.</source>
|
||||||
|
<target>检查此应用程序。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb50000a8fada5672">
|
||||||
|
<source>There was an error in the provider.</source>
|
||||||
|
<target>提供程序中存在一个错误。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s21f95eaf151d4ce3">
|
||||||
|
<source>Review the provider.</source>
|
||||||
|
<target>检查此提供程序。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9fd39a5cb20b4e61">
|
||||||
|
<source>There was an error</source>
|
||||||
|
<target>存在一个错误</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7a6b3453209e1066">
|
||||||
|
<source>There was an error creating the application, but no error message was sent. Please review the server logs.</source>
|
||||||
|
<target>创建应用程序时存在一个错误,但未发送错误消息。请检查服务器日志。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1a711c19cda48375">
|
||||||
|
<source>Configure LDAP Provider</source>
|
||||||
|
<target>配置 LDAP 提供程序</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9368e965b5c292ab">
|
||||||
|
<source>Configure OAuth2/OpenId Provider</source>
|
||||||
|
<target>配置 OAuth2/OpenID 提供程序</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf5cbccdc6254c8dc">
|
||||||
|
<source>Configure Proxy Provider</source>
|
||||||
|
<target>配置代理提供程序</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf6d46bb442b77e91">
|
||||||
|
<source>AdditionalScopes</source>
|
||||||
|
<target>额外的作用域</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s2c8c6f89089b31d4">
|
||||||
|
<source>Configure Radius Provider</source>
|
||||||
|
<target>配置 Radius 提供程序</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfe906cde5dddc041">
|
||||||
|
<source>Configure SAML Provider</source>
|
||||||
|
<target>配置 SAML 提供程序</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb3defbacd01ad972">
|
||||||
|
<source>Property mappings used for user mapping.</source>
|
||||||
|
<target>用于用户映射的属性映射。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7ccce0ec8d228db6">
|
||||||
|
<source>Configure SCIM Provider</source>
|
||||||
|
<target>配置 SCIM 提供程序</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sd7728d2b6e1d25e9">
|
||||||
|
<source>Property mappings used for group creation.</source>
|
||||||
|
<target>用于创建组的属性映射。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7513372fe60f6387">
|
||||||
|
<source>Event volume</source>
|
||||||
|
<target>事件容量</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -5757,12 +5757,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="s848288f8c2265aad">
|
<trans-unit id="s848288f8c2265aad">
|
||||||
<source>Your application has been saved</source>
|
<source>Your application has been saved</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf60f1e5b76897c93">
|
|
||||||
<source>In the Application:</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s7ce65cf482b7bff0">
|
|
||||||
<source>In the Provider:</source>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s67d858051b34c38b">
|
<trans-unit id="s67d858051b34c38b">
|
||||||
<source>Method's display Name.</source>
|
<source>Method's display Name.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
@ -6032,6 +6026,54 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s52bdc80690a9a8dc">
|
<trans-unit id="s52bdc80690a9a8dc">
|
||||||
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="scda8dc24b561e205">
|
||||||
|
<source>There was an error in the application.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdaca9c2c0361ed3a">
|
||||||
|
<source>Review the application.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb50000a8fada5672">
|
||||||
|
<source>There was an error in the provider.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s21f95eaf151d4ce3">
|
||||||
|
<source>Review the provider.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9fd39a5cb20b4e61">
|
||||||
|
<source>There was an error</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7a6b3453209e1066">
|
||||||
|
<source>There was an error creating the application, but no error message was sent. Please review the server logs.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1a711c19cda48375">
|
||||||
|
<source>Configure LDAP Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9368e965b5c292ab">
|
||||||
|
<source>Configure OAuth2/OpenId Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf5cbccdc6254c8dc">
|
||||||
|
<source>Configure Proxy Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf6d46bb442b77e91">
|
||||||
|
<source>AdditionalScopes</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s2c8c6f89089b31d4">
|
||||||
|
<source>Configure Radius Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfe906cde5dddc041">
|
||||||
|
<source>Configure SAML Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb3defbacd01ad972">
|
||||||
|
<source>Property mappings used for user mapping.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7ccce0ec8d228db6">
|
||||||
|
<source>Configure SCIM Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sd7728d2b6e1d25e9">
|
||||||
|
<source>Property mappings used for group creation.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7513372fe60f6387">
|
||||||
|
<source>Event volume</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -7619,14 +7619,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<source>Your application has been saved</source>
|
<source>Your application has been saved</source>
|
||||||
<target>您的应用程序已保存</target>
|
<target>您的应用程序已保存</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf60f1e5b76897c93">
|
|
||||||
<source>In the Application:</source>
|
|
||||||
<target>在应用程序中:</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s7ce65cf482b7bff0">
|
|
||||||
<source>In the Provider:</source>
|
|
||||||
<target>在提供程序中:</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s67d858051b34c38b">
|
<trans-unit id="s67d858051b34c38b">
|
||||||
<source>Method's display Name.</source>
|
<source>Method's display Name.</source>
|
||||||
<target>方法的显示名称。</target>
|
<target>方法的显示名称。</target>
|
||||||
|
@ -7988,6 +7980,70 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="s52bdc80690a9a8dc">
|
<trans-unit id="s52bdc80690a9a8dc">
|
||||||
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
<source>When enabled, the stage will always accept the given user identifier and continue.</source>
|
||||||
<target>启用时,此阶段总是会接受指定的用户 ID 并继续。</target>
|
<target>启用时,此阶段总是会接受指定的用户 ID 并继续。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="scda8dc24b561e205">
|
||||||
|
<source>There was an error in the application.</source>
|
||||||
|
<target>应用程序中存在一个错误。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdaca9c2c0361ed3a">
|
||||||
|
<source>Review the application.</source>
|
||||||
|
<target>检查此应用程序。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb50000a8fada5672">
|
||||||
|
<source>There was an error in the provider.</source>
|
||||||
|
<target>提供程序中存在一个错误。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s21f95eaf151d4ce3">
|
||||||
|
<source>Review the provider.</source>
|
||||||
|
<target>检查此提供程序。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9fd39a5cb20b4e61">
|
||||||
|
<source>There was an error</source>
|
||||||
|
<target>存在一个错误</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7a6b3453209e1066">
|
||||||
|
<source>There was an error creating the application, but no error message was sent. Please review the server logs.</source>
|
||||||
|
<target>创建应用程序时存在一个错误,但未发送错误消息。请检查服务器日志。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1a711c19cda48375">
|
||||||
|
<source>Configure LDAP Provider</source>
|
||||||
|
<target>配置 LDAP 提供程序</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9368e965b5c292ab">
|
||||||
|
<source>Configure OAuth2/OpenId Provider</source>
|
||||||
|
<target>配置 OAuth2/OpenID 提供程序</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf5cbccdc6254c8dc">
|
||||||
|
<source>Configure Proxy Provider</source>
|
||||||
|
<target>配置代理提供程序</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf6d46bb442b77e91">
|
||||||
|
<source>AdditionalScopes</source>
|
||||||
|
<target>额外的作用域</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s2c8c6f89089b31d4">
|
||||||
|
<source>Configure Radius Provider</source>
|
||||||
|
<target>配置 Radius 提供程序</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfe906cde5dddc041">
|
||||||
|
<source>Configure SAML Provider</source>
|
||||||
|
<target>配置 SAML 提供程序</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb3defbacd01ad972">
|
||||||
|
<source>Property mappings used for user mapping.</source>
|
||||||
|
<target>用于用户映射的属性映射。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7ccce0ec8d228db6">
|
||||||
|
<source>Configure SCIM Provider</source>
|
||||||
|
<target>配置 SCIM 提供程序</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sd7728d2b6e1d25e9">
|
||||||
|
<source>Property mappings used for group creation.</source>
|
||||||
|
<target>用于创建组的属性映射。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7513372fe60f6387">
|
||||||
|
<source>Event volume</source>
|
||||||
|
<target>事件容量</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -7555,14 +7555,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<source>Your application has been saved</source>
|
<source>Your application has been saved</source>
|
||||||
<target>已經儲存您的應用程式</target>
|
<target>已經儲存您的應用程式</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sf60f1e5b76897c93">
|
|
||||||
<source>In the Application:</source>
|
|
||||||
<target>在應用程式:</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s7ce65cf482b7bff0">
|
|
||||||
<source>In the Provider:</source>
|
|
||||||
<target>在供應商:</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="s67d858051b34c38b">
|
<trans-unit id="s67d858051b34c38b">
|
||||||
<source>Method's display Name.</source>
|
<source>Method's display Name.</source>
|
||||||
<target>方法的顯示名稱。</target>
|
<target>方法的顯示名稱。</target>
|
||||||
|
@ -7925,6 +7917,54 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s5d7748b1d2363478">
|
<trans-unit id="s5d7748b1d2363478">
|
||||||
<source>Are you sure you want to remove the selected users from the group <x id="0" equiv-text="${this.targetGroup?.name}"/>?</source>
|
<source>Are you sure you want to remove the selected users from the group <x id="0" equiv-text="${this.targetGroup?.name}"/>?</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="scda8dc24b561e205">
|
||||||
|
<source>There was an error in the application.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sdaca9c2c0361ed3a">
|
||||||
|
<source>Review the application.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb50000a8fada5672">
|
||||||
|
<source>There was an error in the provider.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s21f95eaf151d4ce3">
|
||||||
|
<source>Review the provider.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9fd39a5cb20b4e61">
|
||||||
|
<source>There was an error</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7a6b3453209e1066">
|
||||||
|
<source>There was an error creating the application, but no error message was sent. Please review the server logs.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1a711c19cda48375">
|
||||||
|
<source>Configure LDAP Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s9368e965b5c292ab">
|
||||||
|
<source>Configure OAuth2/OpenId Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf5cbccdc6254c8dc">
|
||||||
|
<source>Configure Proxy Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sf6d46bb442b77e91">
|
||||||
|
<source>AdditionalScopes</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s2c8c6f89089b31d4">
|
||||||
|
<source>Configure Radius Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sfe906cde5dddc041">
|
||||||
|
<source>Configure SAML Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sb3defbacd01ad972">
|
||||||
|
<source>Property mappings used for user mapping.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7ccce0ec8d228db6">
|
||||||
|
<source>Configure SCIM Provider</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sd7728d2b6e1d25e9">
|
||||||
|
<source>Property mappings used for group creation.</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s7513372fe60f6387">
|
||||||
|
<source>Event volume</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
|
@ -98,7 +98,7 @@ The challenges for multi-repos are basically the inverse of the advantages of a
|
||||||
|
|
||||||
We are still a small company here at Authentik Security, moving fast to grow our product’s feature set and leap-frog our competitors, while staying true to our open source origins. We want to innovate, release, and at this stage, tilt towards rapid development. Our engineers and infrastructure team have the ability and desire to collaborate closely and learn from one another; this culture is important going forward, and using a monorepo works as a compelling incentive for team transparency and support across the projects.
|
We are still a small company here at Authentik Security, moving fast to grow our product’s feature set and leap-frog our competitors, while staying true to our open source origins. We want to innovate, release, and at this stage, tilt towards rapid development. Our engineers and infrastructure team have the ability and desire to collaborate closely and learn from one another; this culture is important going forward, and using a monorepo works as a compelling incentive for team transparency and support across the projects.
|
||||||
|
|
||||||
The history of authenik provides some additional insight into our use of a monorepo; as the single maintainer for many years, a monorepo was simply easier for me to manage, and even as our wonderful community grew and contributions increased and sponsors appeared, the benefits of a monorepo remain.
|
The history of authentik provides some additional insight into our use of a monorepo; as the single maintainer for many years, a monorepo was simply easier for me to manage, and even as our wonderful community grew and contributions increased and sponsors appeared, the benefits of a monorepo remain.
|
||||||
|
|
||||||
So for us, at this stage, we benefit greatly from using a monorepo for the vast majority of our code base (and documentation). Using a monorepo means that as a team, we closely integrate our work: coding, documentation, test suites, refactoring efforts, common dependencies and tooling.
|
So for us, at this stage, we benefit greatly from using a monorepo for the vast majority of our code base (and documentation). Using a monorepo means that as a team, we closely integrate our work: coding, documentation, test suites, refactoring efforts, common dependencies and tooling.
|
||||||
|
|
||||||
|
|
|
@ -155,4 +155,4 @@ In another example, a breach at Okta (not [that one](https://goauthentik.io/blog
|
||||||
|
|
||||||
The two types of breaches above will drive further interest in zero trust. On the one hand, we see companies fail because they are too trusting; on the other hand, we see other companies fail in some ways but prevent further damage thanks to a few solid elements in their security postures. The potential becomes clear – if companies embrace zero trust, they can do even better.
|
The two types of breaches above will drive further interest in zero trust. On the one hand, we see companies fail because they are too trusting; on the other hand, we see other companies fail in some ways but prevent further damage thanks to a few solid elements in their security postures. The potential becomes clear – if companies embrace zero trust, they can do even better.
|
||||||
|
|
||||||
As always, we look forward to hearing your thoughts! Send us an email at hello@goauthentik, or join us on on [Github](https://github.com/goauthentik/authentik) or [Discord](https://discord.com/invite/jg33eMhnj6).
|
As always, we look forward to hearing your thoughts! Send us an email at hello@goauthentik.io, or join us on on [Github](https://github.com/goauthentik/authentik) or [Discord](https://discord.com/invite/jg33eMhnj6).
|
||||||
|
|
|
@ -30,7 +30,7 @@ hide_table_of_contents: false
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
There was an article recently about nearly 20 well-known startups’ first 10 hires—security engineers didn’t feature at all. Our third hire at Authentik Security was a security engineer so we might be biased, but even startups without the resources for a full-time security hire should have someone on your founding team wearing the security hat, so you get started on the right foot.
|
There was an article recently about nearly 20 well-known startups’ [first 10 hires](https://www.lennysnewsletter.com/p/hiring-your-early-team-b2b)—security engineers didn’t feature at all. Our third hire at Authentik Security was a security engineer so we might be biased, but even startups without the resources for a full-time security hire should have someone on your founding team wearing the security hat, so you get started on the right foot.
|
||||||
|
|
||||||
As security departments are cost centers (not revenue generators) it’s not unusual for startups to take a tightwad mentality with security. The good news is that you don’t need a big budget to have a good security posture. There are plenty of free and open source tools at your disposal, and a lot of what makes good security is actually organizational practices—many of which don’t cost a thing to implement.
|
As security departments are cost centers (not revenue generators) it’s not unusual for startups to take a tightwad mentality with security. The good news is that you don’t need a big budget to have a good security posture. There are plenty of free and open source tools at your disposal, and a lot of what makes good security is actually organizational practices—many of which don’t cost a thing to implement.
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,163 @@
|
||||||
|
---
|
||||||
|
title: Automated security versus the security mindset
|
||||||
|
description: “Automated security plays a key part in many cybersecurity tasks. But what are its failings and will a security mindset always require the human factor?”
|
||||||
|
slug: 2023-11-30-automated-security-versus-the-security-mindset
|
||||||
|
authors:
|
||||||
|
- name: Jens Langhammer
|
||||||
|
title: CTO at Authentik Security Inc
|
||||||
|
url: https://github.com/BeryJu
|
||||||
|
image_url: https://github.com/BeryJu.png
|
||||||
|
tags:
|
||||||
|
- authentik
|
||||||
|
- automated security
|
||||||
|
- security mindset
|
||||||
|
- incident response
|
||||||
|
- vulnerabilities
|
||||||
|
- human factor in cybersecurity
|
||||||
|
- SSO
|
||||||
|
- identity provider
|
||||||
|
- authentication
|
||||||
|
- Authentik Security
|
||||||
|
hide_table_of_contents: false
|
||||||
|
image: ./authentication.png
|
||||||
|
---
|
||||||
|
|
||||||
|
> **_authentik is an open source Identity Provider that unifies your identity needs into a single platform, replacing Okta, Active Directory, and auth0. Authentik Security is a [public benefit company](https://github.com/OpenCoreVentures/ocv-public-benefit-company/blob/main/ocv-public-benefit-company-charter.md) building on top of the open source project._**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Automation plays a large and increasingly important role in cybersecurity. Cybersecurity vendors promote their Machine Learning and Artificial Intelligence products as the inevitable future. However, thanks to the work of security experts like [Bruce Schneier](https://en.wikipedia.org/wiki/Bruce_Schneier), we have more insight into the human adversaries that create the underlying risks to network security, and a better understanding of why teaching humans to have a security mindset is the critical first step to keeping your network safe.
|
||||||
|
|
||||||
|
> The best response to these malicious actors is to think like a security expert and develop the security mindset.
|
||||||
|
|
||||||
|
In this blog post, we examine why automation is such a popular solution to cybersecurity problems—from vulnerability scanning to risk assessments. Then, we will look at those tasks in which security automation by itself proves inadequate, with particular focus on automatic scanning. Next, we make a positive case for why the human factor will always be needed in security. Finally, we will propose that good security isn't a feature. It's a proactive security mindset that's required—one with a human element at its core.
|
||||||
|
|
||||||
|
![authentik UI](./authentication.png)
|
||||||
|
|
||||||
|
<!--truncate-->
|
||||||
|
|
||||||
|
## Why automate security in the first place?
|
||||||
|
|
||||||
|
Automated security is such a popular option purely because of the current dynamics:
|
||||||
|
|
||||||
|
- On the one hand, there is a growing number of security incidents, instigated by systematic threat actors who may use the exact same auto security testing tools to find and target weaknesses
|
||||||
|
- On the other, there is a shortage of trained cybersecurity professionals with adequate time resources to deal with those threats
|
||||||
|
|
||||||
|
Meanwhile, companies concerned about the security of their networks are facing the demands of savvy insurers keen to reduce their risks, while CISOs are coming under increasing personal pressure, considering some have faced new warnings of personal liabilities (including jail time, as we wrote about in a [recent blog](https://goauthentik.io/blog/2023-11-22-how-we-saved-over-100k#repercussions)) from government legislators.
|
||||||
|
|
||||||
|
But it's not just a personnel problem. The nature of some cybersecurity approaches, such as penetration testing, also plays a part. Many of a security engineer’s tasks are repetitive and prolonged. Automated security testing means time can be freed up to make the best use of an internal security engineer or external pentester's resources.
|
||||||
|
|
||||||
|
Finally, it is impossible to deny that securing the perimeter (running regular scans for misconfigurations and unusual behavior) and enforcing robust security policies are all impossible to deploy without some automation. 24/7/365 monitoring, processing massive data sets, and rapid threat detection and remediation all call for significant automated elements. Automated security is also key in helping scale cybersecurity operations to match company, staffing, system, and platform growth.
|
||||||
|
|
||||||
|
### What is the role of automation in security tasks?
|
||||||
|
|
||||||
|
Let’s not throw the baby out with the bath water. Automation has a place and positive role to play in cybersecurity. Auto security testing tools are best deployed for tasks that are repetitive and routine, and that require high volume processing.
|
||||||
|
|
||||||
|
Examples of these tasks include:
|
||||||
|
|
||||||
|
- Scheduled tasks such as vulnerability scanning
|
||||||
|
- 24/7 user and other activity monitoring
|
||||||
|
- Actions that require speed such as detecting and immediately responding to malicious intrusions
|
||||||
|
|
||||||
|
Removing tasks like these from the manual operations of your SOC (security operations center) aids efficiency, supports your security team, and helps ameliorate any skills shortage.
|
||||||
|
|
||||||
|
What are the benefits of an automated security system?
|
||||||
|
|
||||||
|
Automated security also excels in:
|
||||||
|
|
||||||
|
- Reducing human error
|
||||||
|
- Eliminating manual steps
|
||||||
|
- Lowering the number of false positives
|
||||||
|
- Updating software
|
||||||
|
- Helping with compliance
|
||||||
|
- Enhancing incident response and threat intelligence
|
||||||
|
|
||||||
|
## Why automation is a threat to cybersecurity
|
||||||
|
|
||||||
|
If automation is such a popular and necessary asset in the cybersecurity field, why can't we automate everything?
|
||||||
|
|
||||||
|
_Let’s think: Could over-reliance on automated security testing ultimately prove detrimental to cybersecurity and threaten the safety of your systems?_
|
||||||
|
|
||||||
|
To help avoid this, we need to acknowledge that automation can't:
|
||||||
|
|
||||||
|
- Keep security teams up to date with new standards, such as the NIST Cybersecurity Framework; the ISO/IEC 27001 standard for information security management; the CIS Critical Security Controls; the OSSTMM; the Web Application Security Consortium (WASC 2.0); or the finance standard of PCI Data Security Standards for the payment card industry
|
||||||
|
- Adjust your internal security policies and practices to all the nuances of relevant industry, country or regulations such as NIST SP 800-52; The California Consumer Privacy Act: the Canadian PIPEDA; the EU’s GDPR; or HIPAA’s personal health data legislation
|
||||||
|
- Rapidly respond to every new CVE or every item that makes an appearance in the SANS Top 25, or the most common vulnerabilities listed in the OWASP lists
|
||||||
|
- Ensure that your own internal cybersecurity protocols and policies are enforced
|
||||||
|
|
||||||
|
_But what else?_
|
||||||
|
|
||||||
|
The first point to remember is that automated solutions can only reliably alert and respond to the threats to your network, services, databases, APIs, and applications that they've been configured to detect. This configuration is limited to the settings available in the particular software. Automated processes are only as good as the rules human engineers give them. Security processes must still be configured and employed correctly.
|
||||||
|
|
||||||
|
And, your own company’s internal business logic must be factored in. This is where pentesters (who may, of course, rely on some automated tools to help them identify some vulnerabilities across your network) can delve deep on specific vulnerabilities and apply your company’s custom business logic and data breach implications. Resultant summary reports must explain the business, financial, reputational, data, and user implications of likely breaches, investigations and penalties.
|
||||||
|
|
||||||
|
Also, malicious hackers can use automated security techniques just as much as defenders to find potential security flaws in an organization’s network. They use novel attacks inspired by vulnerabilities that automated tools are unable to detect at all, by exploiting mistakes made by users that automation by itself can't solve. Examples include social engineering attacks that can begin with an innocuous looking email, or an SMS or email phishing scam. Given that over 80% of bad actors gain illegitimate entry using social engineering attacks, it is obvious that company-wide staff training is an excellent deployment of resources.
|
||||||
|
|
||||||
|
## Against automatic scanning in favor of a proactive security mindset
|
||||||
|
|
||||||
|
In the case of social engineering attacks that we’ve just mentioned, a security-oriented mindset is what will keep your staff watchful—not the knowledge of automated tools.
|
||||||
|
|
||||||
|
_Could mindset, then, be the greatest weapon in your defensive arsenal? Let’s explore further._
|
||||||
|
|
||||||
|
### What elements are crucial to a security mindset?
|
||||||
|
|
||||||
|
Despite the advantages of automation in security scanning, the element of human expertise is needed in many steps of the scanning process. There is no purely automatic way to proactively identify all new threats and preempt sophisticated or unconventional attacks, for example. Security engineers must wait for the tools on which they rely to be updated with the latest CVEs, and they must then have the expertise to understand the reasons and logic behind threats. They will sometimes have to manually validate these threats where required, then plan mitigation activities for future avoidance.
|
||||||
|
|
||||||
|
Further, it is the practice and discipline of working in cybersecurity that give developers the mindset and expertise to build software that is ‘secure by design’. We expect vulnerabilities, and we write more secure code because of it.
|
||||||
|
|
||||||
|
### Some of the drawbacks of vulnerability scanning tools
|
||||||
|
|
||||||
|
While automated scanning tools can provide a major asset in the arsenal of any cybersecurity professional, we must honestly acknowledge their weaknesses when set side-by-side with a human:
|
||||||
|
|
||||||
|
- An automated scanner can miss vulnerabilities if they are new and not in its database, or if the vulnerability is complex and adaptive. Scanners can only hunt for known vulnerabilities, and according to how automated scans are further configured by users.
|
||||||
|
- The problem of false positives can never be completely eliminated even by the most accurate scanners. In the end, a human expert is needed to filter them out.
|
||||||
|
- Detecting vulnerabilities is only the start. While some scanners assign an urgent priority to their findings, human expertise is needed to assess the _specific_ implications of these vulnerabilities for the platform, system or business.
|
||||||
|
- Once vulnerabilities are detected, fixing and patching them is a manual process. A vulnerability report is a starting point. Identifying a vulnerability is one thing; successfully remediating it is another. Further, security engineers will sometimes also have to further reengineer their code, to ensure a similar problem does not recur.
|
||||||
|
|
||||||
|
Of course, automatic scanners are excellent assets for speed and quick action, repeatability, ease of use, and constant monitoring. They can provide a good starting point for further investigations, not an end point. But they are not equivalent to a full penetration test and can only find risks that are known.
|
||||||
|
|
||||||
|
### What about AI in automated security scanning?
|
||||||
|
|
||||||
|
AI and machine learning contribute to the speed and accuracy of dealing with risks posed by known threats. But, for all these advances, the mind of the security engineer is still required when dealing with unknown or new threats, threats that are chaotic and unpredictable or morphable, and threats that don't follow the rules.
|
||||||
|
|
||||||
|
## The human factor in cybersecurity will always be required
|
||||||
|
|
||||||
|
The fact that there are automated tasks and processes in cybersecurity does not mean that the good security as a whole is autonomous or automatic. Security is more about developing a _security mindset_ than a set of features.
|
||||||
|
|
||||||
|
For further information on the human element in SaaS security, see [Securing the future of SaaS: Enterprise Security and Single Sign-On](https://goauthentik.io/blog/2023-07-28-securing-the-future-of-saas#good-security-cant-be-automated-the-human-element-in-saas-security).
|
||||||
|
|
||||||
|
### Human cyber risk
|
||||||
|
|
||||||
|
Humans are at the forefront of cybercrime. Cyber crimes are committed by human beings using adaptation and innovation to invent fresh attack tactics. It is the human mind that continually develops new techniques to hack, infiltrate, and bypass security systems.
|
||||||
|
|
||||||
|
For example, if your company does not have a 2FA/MFA credential policy, vulnerabilities exist around whether your staff share user credentials to save them time and stress. If these credentials are not updated regularly, or worse, if they’re shared by email, any moderately skilled, malicious hacker could attempt to access the email account of a single user, and use it to find other company passwords. It is these human weaknesses and errors that most bad actors hackers rely on.
|
||||||
|
|
||||||
|
_Over 80% of malicious hacks are as a result of the exploitation of the widest weakness of all—predictable human behavior._
|
||||||
|
|
||||||
|
### Human elements of cybersecurity
|
||||||
|
|
||||||
|
Even in a cybersecurity system that is maximally automated there is human input that can never be removed. Obviously, human experts are needed to guide the automated systems in their functioning. Automation technology depends on humans to set rules and workflows, monitor results over time, and rapidly prioritize then respond to alarming findings.
|
||||||
|
|
||||||
|
Once new and significant threats are detected by the automated security, it is human experts again who have to adjust the performance of the automated system as a response to this changing environment. Any further changes need humans to evaluate the performance of automated systems in real-time. Finally, it is humans who train staff in cyber threat detection for these new dangers.
|
||||||
|
|
||||||
|
### Human-centered cybersecurity
|
||||||
|
|
||||||
|
Despite the growing technology around automated security, and the temptation to relax when it is deployed, there are human factors that are irreplaceable in the practice of cybersecurity. We recently wrote about the importance of the “Blue Team” and how [organizational and product hardening](https://goauthentik.io/blog/2023-11-22-how-we-saved-over-100k#hardening) are an integral part of our human-centered security mindset.
|
||||||
|
|
||||||
|
- The human ability to think creatively and rapidly adapt to changing situations is invaluable to good security processes.
|
||||||
|
- The higher the security risk, the more you need skilled security professionals to supervise the security process.
|
||||||
|
- After automation has quickly gathered information, humans are needed to make any well-informed security and organizational decisions that may arise.
|
||||||
|
- Exclusively human tasks include containment, triage, remediation, and launching new initiatives such as better responses (see [Okta got breached again and they still have not learned their lesson](https://goauthentik.io/blog/2023-10-23-another-okta-breach)).
|
||||||
|
- Only humans can know the commercial implications of a data breach.
|
||||||
|
|
||||||
|
## The security mindset is not a feature
|
||||||
|
|
||||||
|
One misconception is that for every cybersecurity problem or threat, there is an automated feature in some software somewhere that can match it.
|
||||||
|
|
||||||
|
> Some cybersecurity software plans seem to promote feature-rich products but forget to promote highly skilled and aware cybersecurity teams with a proactive security mindset.
|
||||||
|
|
||||||
|
Companies have become too dependent on automation, due to the overwhelming volume of threats and frequency of attacks. This overreliance can cause all sorts of unintended problems—alert fatigue, data overload, devaluing human expertise and input, and an inability to handle zero-day (previously unknown) vulnerabilities.
|
||||||
|
|
||||||
|
Automated security platforms and measures assist and augment human expertise; they do not replace or supersede it. If their corresponding strengths and shortfalls are properly acknowledged, automation and teams with a healthily skeptical security mindset can collaborate for success.
|
||||||
|
|
||||||
|
Let us know if you'd like to learn more about how authentik works as a primary component in a security stack. You can send an email to hello@authentik.io, or find us on [GitHub](https://github.com/goauthentik/authentik) or [Discord](https://discord.com/channels/809154715984199690).
|
|
@ -4,6 +4,10 @@ title: Docker Compose installation
|
||||||
|
|
||||||
This installation method is for test-setups and small-scale production setups.
|
This installation method is for test-setups and small-scale production setups.
|
||||||
|
|
||||||
|
:::info
|
||||||
|
You can also [view a video walk-through](https://www.youtube.com/watch?v=O1qUbrk4Yc8) of the installation process on Docker (with bonus details about email configuration and other important options).
|
||||||
|
:::
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- A host with at least 2 CPU cores and 2 GB of RAM
|
- A host with at least 2 CPU cores and 2 GB of RAM
|
||||||
|
|
|
@ -4,6 +4,10 @@ title: Kubernetes installation
|
||||||
|
|
||||||
You can install authentik to run on Kubernetes using Helm Chart.
|
You can install authentik to run on Kubernetes using Helm Chart.
|
||||||
|
|
||||||
|
:::info
|
||||||
|
You can also [view a video walk-through](https://www.youtube.com/watch?v=O1qUbrk4Yc8) of the installation process on Kubernetes (with bonus details about email configuration and other important options).
|
||||||
|
:::
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
- Kubernetes
|
- Kubernetes
|
||||||
|
|
|
@ -16,6 +16,10 @@ You can configure under which base DN the information should be available. For t
|
||||||
|
|
||||||
Users are available under `ou=users,<base DN>` and groups under `ou=groups,<base DN>`. To aid compatibility, each user belongs to its own "virtual" group, as is standard on most Unix-like systems. This group does not exist in the authentik database, and is generated on the fly. These virtual groups are under the `ou=virtual-groups,<base DN>` DN.
|
Users are available under `ou=users,<base DN>` and groups under `ou=groups,<base DN>`. To aid compatibility, each user belongs to its own "virtual" group, as is standard on most Unix-like systems. This group does not exist in the authentik database, and is generated on the fly. These virtual groups are under the `ou=virtual-groups,<base DN>` DN.
|
||||||
|
|
||||||
|
:::info
|
||||||
|
Note: Every LDAP provider needs to have a unique base DN. You can achieve this by prepending an application-specific OU or DC. e.g. `OU=appname,DC=ldap,DC=goauthentik,DC=io`
|
||||||
|
:::
|
||||||
|
|
||||||
The following fields are currently sent for users:
|
The following fields are currently sent for users:
|
||||||
|
|
||||||
- `cn`: User's username
|
- `cn`: User's username
|
||||||
|
|
|
@ -76,7 +76,7 @@ In Rocket.chat, follow the procedure below:
|
||||||
|
|
||||||
3. In the top right corner, click _Add custom oauth_
|
3. In the top right corner, click _Add custom oauth_
|
||||||
|
|
||||||
4. Give your new oauth the name of _Authenik_, then click _Send_
|
4. Give your new oauth the name of _Authentik_, then click _Send_
|
||||||
|
|
||||||
![](./rocketchat6.png)
|
![](./rocketchat6.png)
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ _I'm only going to list the mandatory/important fields to complete._
|
||||||
|
|
||||||
21. **Application Type:** Web Application
|
21. **Application Type:** Web Application
|
||||||
22. **Name:** Choose a name
|
22. **Name:** Choose a name
|
||||||
23. **Authorized redirect URIs:** `https://authenik.company/source/oauth/callback/google/`
|
23. **Authorized redirect URIs:** `https://authentik.company/source/oauth/callback/google/`
|
||||||
|
|
||||||
![](googledeveloper6.png)
|
![](googledeveloper6.png)
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ You will need to create a new project, and OAuth credentials in the Twitter Deve
|
||||||
|
|
||||||
6. Enable **OAuth 2.0**
|
6. Enable **OAuth 2.0**
|
||||||
7. Set **Type of App** to _Web_
|
7. Set **Type of App** to _Web_
|
||||||
8. Set **Callback URI / Redirect URL** to `https://authenik.company/source/oauth/callback/twitter/`
|
8. Set **Callback URI / Redirect URL** to `https://authentik.company/source/oauth/callback/twitter/`
|
||||||
9. Set **Website URL** to `https://authentik.company`
|
9. Set **Website URL** to `https://authentik.company`
|
||||||
|
|
||||||
![](./twitter2.png)
|
![](./twitter2.png)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -16,23 +16,23 @@
|
||||||
"test": "node --test"
|
"test": "node --test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "3.0.0",
|
"@docusaurus/core": "3.0.1",
|
||||||
"@docusaurus/plugin-client-redirects": "^3.0.0",
|
"@docusaurus/plugin-client-redirects": "^3.0.1",
|
||||||
"@docusaurus/plugin-content-docs": "^3.0.0",
|
"@docusaurus/plugin-content-docs": "^3.0.1",
|
||||||
"@docusaurus/preset-classic": "^3.0.0",
|
"@docusaurus/preset-classic": "^3.0.1",
|
||||||
"@docusaurus/theme-common": "^3.0.0",
|
"@docusaurus/theme-common": "^3.0.1",
|
||||||
"@docusaurus/theme-mermaid": "^3.0.0",
|
"@docusaurus/theme-mermaid": "^3.0.1",
|
||||||
"@mdx-js/react": "^3.0.0",
|
"@mdx-js/react": "^3.0.0",
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
"disqus-react": "^1.1.5",
|
"disqus-react": "^1.1.5",
|
||||||
"postcss": "^8.4.31",
|
"postcss": "^8.4.32",
|
||||||
"prism-react-renderer": "^2.3.0",
|
"prism-react-renderer": "^2.3.0",
|
||||||
"rapidoc": "^9.3.4",
|
"rapidoc": "^9.3.4",
|
||||||
"react-before-after-slider-component": "^1.1.8",
|
"react-before-after-slider-component": "^1.1.8",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-feather": "^2.0.10",
|
"react-feather": "^2.0.10",
|
||||||
"react-toggle": "^4.1.3",
|
"react-toggle": "^4.1.3",
|
||||||
"react-tooltip": "^5.24.0",
|
"react-tooltip": "^5.25.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"remark-github": "^12.0.0"
|
"remark-github": "^12.0.0"
|
||||||
},
|
},
|
||||||
|
@ -49,12 +49,12 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@docusaurus/module-type-aliases": "3.0.0",
|
"@docusaurus/module-type-aliases": "3.0.1",
|
||||||
"@docusaurus/tsconfig": "3.0.0",
|
"@docusaurus/tsconfig": "3.0.1",
|
||||||
"@docusaurus/types": "3.0.0",
|
"@docusaurus/types": "3.0.1",
|
||||||
"@types/react": "^18.2.38",
|
"@types/react": "^18.2.42",
|
||||||
"prettier": "3.1.0",
|
"prettier": "3.1.0",
|
||||||
"typescript": "~5.3.2"
|
"typescript": "~5.3.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
|
|
Reference in New Issue