Merge branch 'main' into multi-tenant-django-tenants
This commit is contained in:
commit
72fcaa92dd
|
@ -61,10 +61,6 @@ jobs:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup authentik env
|
|
||||||
uses: ./.github/actions/setup
|
|
||||||
with:
|
|
||||||
postgresql_version: ${{ matrix.psql }}
|
|
||||||
- name: checkout stable
|
- name: checkout stable
|
||||||
run: |
|
run: |
|
||||||
# Delete all poetry envs
|
# Delete all poetry envs
|
||||||
|
@ -76,7 +72,7 @@ jobs:
|
||||||
git checkout version/$(python -c "from authentik import __version__; print(__version__)")
|
git checkout version/$(python -c "from authentik import __version__; print(__version__)")
|
||||||
rm -rf .github/ scripts/
|
rm -rf .github/ scripts/
|
||||||
mv ../.github ../scripts .
|
mv ../.github ../scripts .
|
||||||
- name: Setup authentik env (ensure stable deps are installed)
|
- name: Setup authentik env (stable)
|
||||||
uses: ./.github/actions/setup
|
uses: ./.github/actions/setup
|
||||||
with:
|
with:
|
||||||
postgresql_version: ${{ matrix.psql }}
|
postgresql_version: ${{ matrix.psql }}
|
||||||
|
@ -90,15 +86,20 @@ jobs:
|
||||||
git clean -d -fx .
|
git clean -d -fx .
|
||||||
git checkout $GITHUB_SHA
|
git checkout $GITHUB_SHA
|
||||||
# Delete previous poetry env
|
# Delete previous poetry env
|
||||||
rm -rf $(poetry env info --path)
|
rm -rf /home/runner/.cache/pypoetry/virtualenvs/*
|
||||||
- name: Setup authentik env (ensure latest deps are installed)
|
- name: Setup authentik env (ensure latest deps are installed)
|
||||||
uses: ./.github/actions/setup
|
uses: ./.github/actions/setup
|
||||||
with:
|
with:
|
||||||
postgresql_version: ${{ matrix.psql }}
|
postgresql_version: ${{ matrix.psql }}
|
||||||
- name: migrate to latest
|
- name: migrate to latest
|
||||||
run: |
|
run: |
|
||||||
poetry install
|
|
||||||
poetry run python -m lifecycle.migrate
|
poetry run python -m lifecycle.migrate
|
||||||
|
- name: run tests
|
||||||
|
env:
|
||||||
|
# Test in the main database that we just migrated from the previous stable version
|
||||||
|
AUTHENTIK_POSTGRESQL__TEST__NAME: authentik
|
||||||
|
run: |
|
||||||
|
poetry run make test
|
||||||
test-unittest:
|
test-unittest:
|
||||||
name: test-unittest - PostgreSQL ${{ matrix.psql }}
|
name: test-unittest - PostgreSQL ${{ matrix.psql }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
5
Makefile
5
Makefile
|
@ -115,8 +115,9 @@ gen-diff: ## (Release) generate the changelog diff between the current schema a
|
||||||
npx prettier --write diff.md
|
npx prettier --write diff.md
|
||||||
|
|
||||||
gen-clean:
|
gen-clean:
|
||||||
rm -rf web/api/src/
|
rm -rf gen-go-api/
|
||||||
rm -rf api/
|
rm -rf gen-ts-api/
|
||||||
|
rm -rf web/node_modules/@goauthentik/api/
|
||||||
|
|
||||||
gen-client-ts: ## Build and install the authentik API for Typescript into the authentik UI Application
|
gen-client-ts: ## Build and install the authentik API for Typescript into the authentik UI Application
|
||||||
docker run \
|
docker run \
|
||||||
|
|
|
@ -12,6 +12,8 @@ from authentik.blueprints.tests import reconcile_app
|
||||||
from authentik.core.models import Token, TokenIntents, User, UserTypes
|
from authentik.core.models import Token, TokenIntents, User, UserTypes
|
||||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||||
from authentik.lib.generators import generate_id
|
from authentik.lib.generators import generate_id
|
||||||
|
from authentik.outposts.apps import MANAGED_OUTPOST
|
||||||
|
from authentik.outposts.models import Outpost
|
||||||
from authentik.providers.oauth2.constants import SCOPE_AUTHENTIK_API
|
from authentik.providers.oauth2.constants import SCOPE_AUTHENTIK_API
|
||||||
from authentik.providers.oauth2.models import AccessToken, OAuth2Provider
|
from authentik.providers.oauth2.models import AccessToken, OAuth2Provider
|
||||||
|
|
||||||
|
@ -49,8 +51,12 @@ class TestAPIAuth(TestCase):
|
||||||
with self.assertRaises(AuthenticationFailed):
|
with self.assertRaises(AuthenticationFailed):
|
||||||
bearer_auth(f"Bearer {token.key}".encode())
|
bearer_auth(f"Bearer {token.key}".encode())
|
||||||
|
|
||||||
def test_managed_outpost(self):
|
@reconcile_app("authentik_outposts")
|
||||||
|
def test_managed_outpost_fail(self):
|
||||||
"""Test managed outpost"""
|
"""Test managed outpost"""
|
||||||
|
outpost = Outpost.objects.filter(managed=MANAGED_OUTPOST).first()
|
||||||
|
outpost.user.delete()
|
||||||
|
outpost.delete()
|
||||||
with self.assertRaises(AuthenticationFailed):
|
with self.assertRaises(AuthenticationFailed):
|
||||||
bearer_auth(f"Bearer {settings.SECRET_KEY}".encode())
|
bearer_auth(f"Bearer {settings.SECRET_KEY}".encode())
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from drf_spectacular.utils import extend_schema, inline_serializer
|
from drf_spectacular.utils import extend_schema, inline_serializer
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.fields import CharField, DateTimeField, JSONField
|
from rest_framework.fields import CharField, DateTimeField
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.serializers import ListSerializer, ModelSerializer
|
from rest_framework.serializers import ListSerializer, ModelSerializer
|
||||||
|
@ -15,7 +15,7 @@ from authentik.blueprints.v1.importer import Importer
|
||||||
from authentik.blueprints.v1.oci import OCI_PREFIX
|
from authentik.blueprints.v1.oci import OCI_PREFIX
|
||||||
from authentik.blueprints.v1.tasks import apply_blueprint, blueprints_find_dict
|
from authentik.blueprints.v1.tasks import apply_blueprint, blueprints_find_dict
|
||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.core.api.utils import PassiveSerializer
|
from authentik.core.api.utils import JSONDictField, PassiveSerializer
|
||||||
|
|
||||||
|
|
||||||
class ManagedSerializer:
|
class ManagedSerializer:
|
||||||
|
@ -28,7 +28,7 @@ class MetadataSerializer(PassiveSerializer):
|
||||||
"""Serializer for blueprint metadata"""
|
"""Serializer for blueprint metadata"""
|
||||||
|
|
||||||
name = CharField()
|
name = CharField()
|
||||||
labels = JSONField()
|
labels = JSONDictField()
|
||||||
|
|
||||||
|
|
||||||
class BlueprintInstanceSerializer(ModelSerializer):
|
class BlueprintInstanceSerializer(ModelSerializer):
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.fields import BooleanField, JSONField
|
from rest_framework.fields import BooleanField
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.blueprints.v1.meta.registry import BaseMetaModel, MetaResult, registry
|
from authentik.blueprints.v1.meta.registry import BaseMetaModel, MetaResult, registry
|
||||||
from authentik.core.api.utils import PassiveSerializer, is_dict
|
from authentik.core.api.utils import JSONDictField, PassiveSerializer
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from authentik.blueprints.models import BlueprintInstance
|
from authentik.blueprints.models import BlueprintInstance
|
||||||
|
@ -17,7 +17,7 @@ LOGGER = get_logger()
|
||||||
class ApplyBlueprintMetaSerializer(PassiveSerializer):
|
class ApplyBlueprintMetaSerializer(PassiveSerializer):
|
||||||
"""Serializer for meta apply blueprint model"""
|
"""Serializer for meta apply blueprint model"""
|
||||||
|
|
||||||
identifiers = JSONField(validators=[is_dict])
|
identifiers = JSONDictField()
|
||||||
required = BooleanField(default=True)
|
required = BooleanField(default=True)
|
||||||
|
|
||||||
# We cannot override `instance` as that will confuse rest_framework
|
# We cannot override `instance` as that will confuse rest_framework
|
||||||
|
|
|
@ -8,7 +8,7 @@ from django_filters.filterset import FilterSet
|
||||||
from drf_spectacular.utils import OpenApiResponse, extend_schema
|
from drf_spectacular.utils import OpenApiResponse, extend_schema
|
||||||
from guardian.shortcuts import get_objects_for_user
|
from guardian.shortcuts import get_objects_for_user
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.fields import CharField, IntegerField, JSONField
|
from rest_framework.fields import CharField, IntegerField
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.serializers import ListSerializer, ModelSerializer, ValidationError
|
from rest_framework.serializers import ListSerializer, ModelSerializer, ValidationError
|
||||||
|
@ -16,7 +16,7 @@ from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
from authentik.api.decorators import permission_required
|
from authentik.api.decorators import permission_required
|
||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.core.api.utils import PassiveSerializer, is_dict
|
from authentik.core.api.utils import JSONDictField, PassiveSerializer
|
||||||
from authentik.core.models import Group, User
|
from authentik.core.models import Group, User
|
||||||
from authentik.rbac.api.roles import RoleSerializer
|
from authentik.rbac.api.roles import RoleSerializer
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ from authentik.rbac.api.roles import RoleSerializer
|
||||||
class GroupMemberSerializer(ModelSerializer):
|
class GroupMemberSerializer(ModelSerializer):
|
||||||
"""Stripped down user serializer to show relevant users for groups"""
|
"""Stripped down user serializer to show relevant users for groups"""
|
||||||
|
|
||||||
attributes = JSONField(validators=[is_dict], required=False)
|
attributes = JSONDictField(required=False)
|
||||||
uid = CharField(read_only=True)
|
uid = CharField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -44,7 +44,7 @@ class GroupMemberSerializer(ModelSerializer):
|
||||||
class GroupSerializer(ModelSerializer):
|
class GroupSerializer(ModelSerializer):
|
||||||
"""Group Serializer"""
|
"""Group Serializer"""
|
||||||
|
|
||||||
attributes = JSONField(validators=[is_dict], required=False)
|
attributes = JSONDictField(required=False)
|
||||||
users_obj = ListSerializer(
|
users_obj = ListSerializer(
|
||||||
child=GroupMemberSerializer(), read_only=True, source="users", required=False
|
child=GroupMemberSerializer(), read_only=True, source="users", required=False
|
||||||
)
|
)
|
||||||
|
|
|
@ -32,13 +32,7 @@ from drf_spectacular.utils import (
|
||||||
)
|
)
|
||||||
from guardian.shortcuts import get_anonymous_user, get_objects_for_user
|
from guardian.shortcuts import get_anonymous_user, get_objects_for_user
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.fields import (
|
from rest_framework.fields import CharField, IntegerField, ListField, SerializerMethodField
|
||||||
CharField,
|
|
||||||
IntegerField,
|
|
||||||
JSONField,
|
|
||||||
ListField,
|
|
||||||
SerializerMethodField,
|
|
||||||
)
|
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.serializers import (
|
from rest_framework.serializers import (
|
||||||
|
@ -58,7 +52,7 @@ from authentik.api.decorators import permission_required
|
||||||
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
|
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
|
||||||
from authentik.brands.models import Brand
|
from authentik.brands.models import Brand
|
||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.core.api.utils import LinkSerializer, PassiveSerializer, is_dict
|
from authentik.core.api.utils import JSONDictField, LinkSerializer, PassiveSerializer
|
||||||
from authentik.core.middleware import (
|
from authentik.core.middleware import (
|
||||||
SESSION_KEY_IMPERSONATE_ORIGINAL_USER,
|
SESSION_KEY_IMPERSONATE_ORIGINAL_USER,
|
||||||
SESSION_KEY_IMPERSONATE_USER,
|
SESSION_KEY_IMPERSONATE_USER,
|
||||||
|
@ -88,7 +82,7 @@ LOGGER = get_logger()
|
||||||
class UserGroupSerializer(ModelSerializer):
|
class UserGroupSerializer(ModelSerializer):
|
||||||
"""Simplified Group Serializer for user's groups"""
|
"""Simplified Group Serializer for user's groups"""
|
||||||
|
|
||||||
attributes = JSONField(required=False)
|
attributes = JSONDictField(required=False)
|
||||||
parent_name = CharField(source="parent.name", read_only=True)
|
parent_name = CharField(source="parent.name", read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -109,7 +103,7 @@ class UserSerializer(ModelSerializer):
|
||||||
|
|
||||||
is_superuser = BooleanField(read_only=True)
|
is_superuser = BooleanField(read_only=True)
|
||||||
avatar = CharField(read_only=True)
|
avatar = CharField(read_only=True)
|
||||||
attributes = JSONField(validators=[is_dict], required=False)
|
attributes = JSONDictField(required=False)
|
||||||
groups = PrimaryKeyRelatedField(
|
groups = PrimaryKeyRelatedField(
|
||||||
allow_empty=True, many=True, source="ak_groups", queryset=Group.objects.all(), default=list
|
allow_empty=True, many=True, source="ak_groups", queryset=Group.objects.all(), default=list
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from django.db.models import Model
|
from django.db.models import Model
|
||||||
|
from drf_spectacular.extensions import OpenApiSerializerFieldExtension
|
||||||
|
from drf_spectacular.plumbing import build_basic_type
|
||||||
|
from drf_spectacular.types import OpenApiTypes
|
||||||
from rest_framework.fields import CharField, IntegerField, JSONField
|
from rest_framework.fields import CharField, IntegerField, JSONField
|
||||||
from rest_framework.serializers import Serializer, SerializerMethodField, ValidationError
|
from rest_framework.serializers import Serializer, SerializerMethodField, ValidationError
|
||||||
|
|
||||||
|
@ -13,6 +16,21 @@ def is_dict(value: Any):
|
||||||
raise ValidationError("Value must be a dictionary, and not have any duplicate keys.")
|
raise ValidationError("Value must be a dictionary, and not have any duplicate keys.")
|
||||||
|
|
||||||
|
|
||||||
|
class JSONDictField(JSONField):
|
||||||
|
"""JSON Field which only allows dictionaries"""
|
||||||
|
|
||||||
|
default_validators = [is_dict]
|
||||||
|
|
||||||
|
|
||||||
|
class JSONExtension(OpenApiSerializerFieldExtension):
|
||||||
|
"""Generate API Schema for JSON fields as"""
|
||||||
|
|
||||||
|
target_class = "authentik.core.api.utils.JSONDictField"
|
||||||
|
|
||||||
|
def map_serializer_field(self, auto_schema, direction):
|
||||||
|
return build_basic_type(OpenApiTypes.OBJECT)
|
||||||
|
|
||||||
|
|
||||||
class PassiveSerializer(Serializer):
|
class PassiveSerializer(Serializer):
|
||||||
"""Base serializer class which doesn't implement create/update methods"""
|
"""Base serializer class which doesn't implement create/update methods"""
|
||||||
|
|
||||||
|
@ -26,7 +44,7 @@ class PassiveSerializer(Serializer):
|
||||||
class PropertyMappingPreviewSerializer(PassiveSerializer):
|
class PropertyMappingPreviewSerializer(PassiveSerializer):
|
||||||
"""Preview how the current user is mapped via the property mappings selected in a provider"""
|
"""Preview how the current user is mapped via the property mappings selected in a provider"""
|
||||||
|
|
||||||
preview = JSONField(read_only=True)
|
preview = JSONDictField(read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class MetaNameSerializer(PassiveSerializer):
|
class MetaNameSerializer(PassiveSerializer):
|
||||||
|
|
|
@ -30,7 +30,6 @@ from authentik.lib.models import (
|
||||||
DomainlessFormattedURLValidator,
|
DomainlessFormattedURLValidator,
|
||||||
SerializerModel,
|
SerializerModel,
|
||||||
)
|
)
|
||||||
from authentik.lib.utils.http import get_client_ip
|
|
||||||
from authentik.policies.models import PolicyBindingModel
|
from authentik.policies.models import PolicyBindingModel
|
||||||
from authentik.root.install_id import get_install_id
|
from authentik.root.install_id import get_install_id
|
||||||
|
|
||||||
|
@ -748,12 +747,14 @@ class AuthenticatedSession(ExpiringModel):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_request(request: HttpRequest, user: User) -> Optional["AuthenticatedSession"]:
|
def from_request(request: HttpRequest, user: User) -> Optional["AuthenticatedSession"]:
|
||||||
"""Create a new session from a http request"""
|
"""Create a new session from a http request"""
|
||||||
|
from authentik.root.middleware import ClientIPMiddleware
|
||||||
|
|
||||||
if not hasattr(request, "session") or not request.session.session_key:
|
if not hasattr(request, "session") or not request.session.session_key:
|
||||||
return None
|
return None
|
||||||
return AuthenticatedSession(
|
return AuthenticatedSession(
|
||||||
session_key=request.session.session_key,
|
session_key=request.session.session_key,
|
||||||
user=user,
|
user=user,
|
||||||
last_ip=get_client_ip(request),
|
last_ip=ClientIPMiddleware.get_client_ip(request),
|
||||||
last_user_agent=request.META.get("HTTP_USER_AGENT", ""),
|
last_user_agent=request.META.get("HTTP_USER_AGENT", ""),
|
||||||
expires=request.session.get_expiry_date(),
|
expires=request.session.get_expiry_date(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -44,28 +44,14 @@
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="pf-c-background-image">
|
<div class="pf-c-background-image">
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
<ak-message-container></ak-message-container>
|
<ak-message-container></ak-message-container>
|
||||||
<div class="pf-c-login">
|
<div class="pf-c-login stacked">
|
||||||
<div class="ak-login-container">
|
<div class="ak-login-container">
|
||||||
<header class="pf-c-login__header">
|
<main class="pf-c-login__main">
|
||||||
<div class="pf-c-brand ak-brand">
|
<div class="pf-c-login__main-header pf-c-brand ak-brand">
|
||||||
<img src="{{ brand.branding_logo }}" alt="authentik Logo" />
|
<img src="{{ brand.branding_logo }}" alt="authentik Logo" />
|
||||||
</div>
|
</div>
|
||||||
</header>
|
|
||||||
{% block main_container %}
|
|
||||||
<main class="pf-c-login__main">
|
|
||||||
<header class="pf-c-login__main-header">
|
<header class="pf-c-login__main-header">
|
||||||
<h1 class="pf-c-title pf-m-3xl">
|
<h1 class="pf-c-title pf-m-3xl">
|
||||||
{% block card_title %}
|
{% block card_title %}
|
||||||
|
@ -77,7 +63,6 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
|
||||||
<footer class="pf-c-login__footer">
|
<footer class="pf-c-login__footer">
|
||||||
<ul class="pf-c-list pf-m-inline">
|
<ul class="pf-c-list pf-m-inline">
|
||||||
{% for link in footer_links %}
|
{% for link in footer_links %}
|
||||||
|
|
|
@ -38,9 +38,10 @@ from authentik.events.utils import (
|
||||||
)
|
)
|
||||||
from authentik.lib.models import DomainlessURLValidator, SerializerModel
|
from authentik.lib.models import DomainlessURLValidator, SerializerModel
|
||||||
from authentik.lib.sentry import SentryIgnoredException
|
from authentik.lib.sentry import SentryIgnoredException
|
||||||
from authentik.lib.utils.http import get_client_ip, get_http_session
|
from authentik.lib.utils.http import get_http_session
|
||||||
from authentik.lib.utils.time import timedelta_from_string
|
from authentik.lib.utils.time import timedelta_from_string
|
||||||
from authentik.policies.models import PolicyBindingModel
|
from authentik.policies.models import PolicyBindingModel
|
||||||
|
from authentik.root.middleware import ClientIPMiddleware
|
||||||
from authentik.stages.email.utils import TemplateEmailMessage
|
from authentik.stages.email.utils import TemplateEmailMessage
|
||||||
from authentik.tenants.models import Tenant
|
from authentik.tenants.models import Tenant
|
||||||
|
|
||||||
|
@ -247,7 +248,7 @@ class Event(SerializerModel, ExpiringModel):
|
||||||
self.user = get_user(request.session[SESSION_KEY_IMPERSONATE_ORIGINAL_USER])
|
self.user = get_user(request.session[SESSION_KEY_IMPERSONATE_ORIGINAL_USER])
|
||||||
self.user["on_behalf_of"] = get_user(request.session[SESSION_KEY_IMPERSONATE_USER])
|
self.user["on_behalf_of"] = get_user(request.session[SESSION_KEY_IMPERSONATE_USER])
|
||||||
# User 255.255.255.255 as fallback if IP cannot be determined
|
# User 255.255.255.255 as fallback if IP cannot be determined
|
||||||
self.client_ip = get_client_ip(request)
|
self.client_ip = ClientIPMiddleware.get_client_ip(request)
|
||||||
# Apply GeoIP Data, when enabled
|
# Apply GeoIP Data, when enabled
|
||||||
self.with_geoip()
|
self.with_geoip()
|
||||||
# If there's no app set, we get it from the requests too
|
# If there's no app set, we get it from the requests too
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Generated by Django 4.2.6 on 2023-10-28 14:24
|
# Generated by Django 4.2.6 on 2023-10-28 14:24
|
||||||
|
|
||||||
from django.apps.registry import Apps
|
from django.apps.registry import Apps
|
||||||
from django.db import migrations
|
from django.db import migrations, models
|
||||||
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,4 +31,19 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(set_oobe_flow_authentication),
|
migrations.RunPython(set_oobe_flow_authentication),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="flow",
|
||||||
|
name="authentication",
|
||||||
|
field=models.TextField(
|
||||||
|
choices=[
|
||||||
|
("none", "None"),
|
||||||
|
("require_authenticated", "Require Authenticated"),
|
||||||
|
("require_unauthenticated", "Require Unauthenticated"),
|
||||||
|
("require_superuser", "Require Superuser"),
|
||||||
|
("require_outpost", "Require Outpost"),
|
||||||
|
],
|
||||||
|
default="none",
|
||||||
|
help_text="Required level of authentication and authorization to access a flow.",
|
||||||
|
),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -31,6 +31,7 @@ class FlowAuthenticationRequirement(models.TextChoices):
|
||||||
REQUIRE_AUTHENTICATED = "require_authenticated"
|
REQUIRE_AUTHENTICATED = "require_authenticated"
|
||||||
REQUIRE_UNAUTHENTICATED = "require_unauthenticated"
|
REQUIRE_UNAUTHENTICATED = "require_unauthenticated"
|
||||||
REQUIRE_SUPERUSER = "require_superuser"
|
REQUIRE_SUPERUSER = "require_superuser"
|
||||||
|
REQUIRE_OUTPOST = "require_outpost"
|
||||||
|
|
||||||
|
|
||||||
class NotConfiguredAction(models.TextChoices):
|
class NotConfiguredAction(models.TextChoices):
|
||||||
|
|
|
@ -23,6 +23,7 @@ from authentik.flows.models import (
|
||||||
)
|
)
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.policies.engine import PolicyEngine
|
from authentik.policies.engine import PolicyEngine
|
||||||
|
from authentik.root.middleware import ClientIPMiddleware
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
PLAN_CONTEXT_PENDING_USER = "pending_user"
|
PLAN_CONTEXT_PENDING_USER = "pending_user"
|
||||||
|
@ -141,6 +142,10 @@ class FlowPlanner:
|
||||||
and not request.user.is_superuser
|
and not request.user.is_superuser
|
||||||
):
|
):
|
||||||
raise FlowNonApplicableException()
|
raise FlowNonApplicableException()
|
||||||
|
if self.flow.authentication == FlowAuthenticationRequirement.REQUIRE_OUTPOST:
|
||||||
|
outpost_user = ClientIPMiddleware.get_outpost_user(request)
|
||||||
|
if not outpost_user:
|
||||||
|
raise FlowNonApplicableException()
|
||||||
|
|
||||||
def plan(
|
def plan(
|
||||||
self, request: HttpRequest, default_context: Optional[dict[str, Any]] = None
|
self, request: HttpRequest, default_context: Optional[dict[str, Any]] = None
|
||||||
|
|
|
@ -8,6 +8,7 @@ from django.test import RequestFactory, TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from guardian.shortcuts import get_anonymous_user
|
from guardian.shortcuts import get_anonymous_user
|
||||||
|
|
||||||
|
from authentik.blueprints.tests import reconcile_app
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||||
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
|
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
|
||||||
|
@ -15,9 +16,12 @@ from authentik.flows.markers import ReevaluateMarker, StageMarker
|
||||||
from authentik.flows.models import FlowAuthenticationRequirement, FlowDesignation, FlowStageBinding
|
from authentik.flows.models import FlowAuthenticationRequirement, FlowDesignation, FlowStageBinding
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner, cache_key
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner, cache_key
|
||||||
from authentik.lib.tests.utils import dummy_get_response
|
from authentik.lib.tests.utils import dummy_get_response
|
||||||
|
from authentik.outposts.apps import MANAGED_OUTPOST
|
||||||
|
from authentik.outposts.models import Outpost
|
||||||
from authentik.policies.dummy.models import DummyPolicy
|
from authentik.policies.dummy.models import DummyPolicy
|
||||||
from authentik.policies.models import PolicyBinding
|
from authentik.policies.models import PolicyBinding
|
||||||
from authentik.policies.types import PolicyResult
|
from authentik.policies.types import PolicyResult
|
||||||
|
from authentik.root.middleware import ClientIPMiddleware
|
||||||
from authentik.stages.dummy.models import DummyStage
|
from authentik.stages.dummy.models import DummyStage
|
||||||
|
|
||||||
POLICY_RETURN_FALSE = PropertyMock(return_value=PolicyResult(False))
|
POLICY_RETURN_FALSE = PropertyMock(return_value=PolicyResult(False))
|
||||||
|
@ -68,6 +72,34 @@ class TestFlowPlanner(TestCase):
|
||||||
planner.allow_empty_flows = True
|
planner.allow_empty_flows = True
|
||||||
planner.plan(request)
|
planner.plan(request)
|
||||||
|
|
||||||
|
@reconcile_app("authentik_outposts")
|
||||||
|
def test_authentication_outpost(self):
|
||||||
|
"""Test flow authentication (outpost)"""
|
||||||
|
flow = create_test_flow()
|
||||||
|
flow.authentication = FlowAuthenticationRequirement.REQUIRE_OUTPOST
|
||||||
|
request = self.request_factory.get(
|
||||||
|
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||||
|
)
|
||||||
|
request.user = AnonymousUser()
|
||||||
|
with self.assertRaises(FlowNonApplicableException):
|
||||||
|
planner = FlowPlanner(flow)
|
||||||
|
planner.allow_empty_flows = True
|
||||||
|
planner.plan(request)
|
||||||
|
|
||||||
|
outpost = Outpost.objects.filter(managed=MANAGED_OUTPOST).first()
|
||||||
|
request = self.request_factory.get(
|
||||||
|
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||||
|
HTTP_X_AUTHENTIK_OUTPOST_TOKEN=outpost.token.key,
|
||||||
|
HTTP_X_AUTHENTIK_REMOTE_IP="1.2.3.4",
|
||||||
|
)
|
||||||
|
request.user = AnonymousUser()
|
||||||
|
middleware = ClientIPMiddleware(dummy_get_response)
|
||||||
|
middleware(request)
|
||||||
|
|
||||||
|
planner = FlowPlanner(flow)
|
||||||
|
planner.allow_empty_flows = True
|
||||||
|
planner.plan(request)
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
"authentik.policies.engine.PolicyEngine.result",
|
"authentik.policies.engine.PolicyEngine.result",
|
||||||
POLICY_RETURN_FALSE,
|
POLICY_RETURN_FALSE,
|
||||||
|
|
|
@ -8,6 +8,8 @@ postgresql:
|
||||||
password: "env://POSTGRES_PASSWORD"
|
password: "env://POSTGRES_PASSWORD"
|
||||||
use_pgbouncer: false
|
use_pgbouncer: false
|
||||||
use_pgpool: false
|
use_pgpool: false
|
||||||
|
test:
|
||||||
|
name: test_authentik
|
||||||
|
|
||||||
listen:
|
listen:
|
||||||
listen_http: 0.0.0.0:9000
|
listen_http: 0.0.0.0:9000
|
||||||
|
|
|
@ -3,8 +3,8 @@ from django.test import RequestFactory, TestCase
|
||||||
|
|
||||||
from authentik.core.models import Token, TokenIntents, UserTypes
|
from authentik.core.models import Token, TokenIntents, UserTypes
|
||||||
from authentik.core.tests.utils import create_test_admin_user
|
from authentik.core.tests.utils import create_test_admin_user
|
||||||
from authentik.lib.utils.http import OUTPOST_REMOTE_IP_HEADER, OUTPOST_TOKEN_HEADER, get_client_ip
|
|
||||||
from authentik.lib.views import bad_request_message
|
from authentik.lib.views import bad_request_message
|
||||||
|
from authentik.root.middleware import ClientIPMiddleware
|
||||||
|
|
||||||
|
|
||||||
class TestHTTP(TestCase):
|
class TestHTTP(TestCase):
|
||||||
|
@ -22,12 +22,12 @@ class TestHTTP(TestCase):
|
||||||
def test_normal(self):
|
def test_normal(self):
|
||||||
"""Test normal request"""
|
"""Test normal request"""
|
||||||
request = self.factory.get("/")
|
request = self.factory.get("/")
|
||||||
self.assertEqual(get_client_ip(request), "127.0.0.1")
|
self.assertEqual(ClientIPMiddleware.get_client_ip(request), "127.0.0.1")
|
||||||
|
|
||||||
def test_forward_for(self):
|
def test_forward_for(self):
|
||||||
"""Test x-forwarded-for request"""
|
"""Test x-forwarded-for request"""
|
||||||
request = self.factory.get("/", HTTP_X_FORWARDED_FOR="127.0.0.2")
|
request = self.factory.get("/", HTTP_X_FORWARDED_FOR="127.0.0.2")
|
||||||
self.assertEqual(get_client_ip(request), "127.0.0.2")
|
self.assertEqual(ClientIPMiddleware.get_client_ip(request), "127.0.0.2")
|
||||||
|
|
||||||
def test_fake_outpost(self):
|
def test_fake_outpost(self):
|
||||||
"""Test faked IP which is overridden by an outpost"""
|
"""Test faked IP which is overridden by an outpost"""
|
||||||
|
@ -38,28 +38,28 @@ class TestHTTP(TestCase):
|
||||||
request = self.factory.get(
|
request = self.factory.get(
|
||||||
"/",
|
"/",
|
||||||
**{
|
**{
|
||||||
OUTPOST_REMOTE_IP_HEADER: "1.2.3.4",
|
ClientIPMiddleware.outpost_remote_ip_header: "1.2.3.4",
|
||||||
OUTPOST_TOKEN_HEADER: "abc",
|
ClientIPMiddleware.outpost_token_header: "abc",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.assertEqual(get_client_ip(request), "127.0.0.1")
|
self.assertEqual(ClientIPMiddleware.get_client_ip(request), "127.0.0.1")
|
||||||
# Invalid, user doesn't have permissions
|
# Invalid, user doesn't have permissions
|
||||||
request = self.factory.get(
|
request = self.factory.get(
|
||||||
"/",
|
"/",
|
||||||
**{
|
**{
|
||||||
OUTPOST_REMOTE_IP_HEADER: "1.2.3.4",
|
ClientIPMiddleware.outpost_remote_ip_header: "1.2.3.4",
|
||||||
OUTPOST_TOKEN_HEADER: token.key,
|
ClientIPMiddleware.outpost_token_header: token.key,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.assertEqual(get_client_ip(request), "127.0.0.1")
|
self.assertEqual(ClientIPMiddleware.get_client_ip(request), "127.0.0.1")
|
||||||
# Valid
|
# Valid
|
||||||
self.user.type = UserTypes.INTERNAL_SERVICE_ACCOUNT
|
self.user.type = UserTypes.INTERNAL_SERVICE_ACCOUNT
|
||||||
self.user.save()
|
self.user.save()
|
||||||
request = self.factory.get(
|
request = self.factory.get(
|
||||||
"/",
|
"/",
|
||||||
**{
|
**{
|
||||||
OUTPOST_REMOTE_IP_HEADER: "1.2.3.4",
|
ClientIPMiddleware.outpost_remote_ip_header: "1.2.3.4",
|
||||||
OUTPOST_TOKEN_HEADER: token.key,
|
ClientIPMiddleware.outpost_token_header: token.key,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.assertEqual(get_client_ip(request), "1.2.3.4")
|
self.assertEqual(ClientIPMiddleware.get_client_ip(request), "1.2.3.4")
|
||||||
|
|
|
@ -1,82 +1,12 @@
|
||||||
"""http helpers"""
|
"""http helpers"""
|
||||||
from typing import Any, Optional
|
|
||||||
|
|
||||||
from django.http import HttpRequest
|
|
||||||
from requests.sessions import Session
|
from requests.sessions import Session
|
||||||
from sentry_sdk.hub import Hub
|
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik import get_full_version
|
from authentik import get_full_version
|
||||||
|
|
||||||
OUTPOST_REMOTE_IP_HEADER = "HTTP_X_AUTHENTIK_REMOTE_IP"
|
|
||||||
OUTPOST_TOKEN_HEADER = "HTTP_X_AUTHENTIK_OUTPOST_TOKEN" # nosec
|
|
||||||
DEFAULT_IP = "255.255.255.255"
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
|
||||||
|
|
||||||
def _get_client_ip_from_meta(meta: dict[str, Any]) -> str:
|
|
||||||
"""Attempt to get the client's IP by checking common HTTP Headers.
|
|
||||||
Returns none if no IP Could be found
|
|
||||||
|
|
||||||
No additional validation is done here as requests are expected to only arrive here
|
|
||||||
via the go proxy, which deals with validating these headers for us"""
|
|
||||||
headers = (
|
|
||||||
"HTTP_X_FORWARDED_FOR",
|
|
||||||
"REMOTE_ADDR",
|
|
||||||
)
|
|
||||||
for _header in headers:
|
|
||||||
if _header in meta:
|
|
||||||
ips: list[str] = meta.get(_header).split(",")
|
|
||||||
return ips[0].strip()
|
|
||||||
return DEFAULT_IP
|
|
||||||
|
|
||||||
|
|
||||||
def _get_outpost_override_ip(request: HttpRequest) -> Optional[str]:
|
|
||||||
"""Get the actual remote IP when set by an outpost. Only
|
|
||||||
allowed when the request is authenticated, by an outpost internal service account"""
|
|
||||||
from authentik.core.models import Token, TokenIntents, UserTypes
|
|
||||||
|
|
||||||
if OUTPOST_REMOTE_IP_HEADER not in request.META or OUTPOST_TOKEN_HEADER not in request.META:
|
|
||||||
return None
|
|
||||||
fake_ip = request.META[OUTPOST_REMOTE_IP_HEADER]
|
|
||||||
token = (
|
|
||||||
Token.filter_not_expired(
|
|
||||||
key=request.META.get(OUTPOST_TOKEN_HEADER), intent=TokenIntents.INTENT_API
|
|
||||||
)
|
|
||||||
.select_related("user")
|
|
||||||
.first()
|
|
||||||
)
|
|
||||||
if not token:
|
|
||||||
LOGGER.warning("Attempted remote-ip override without token", fake_ip=fake_ip)
|
|
||||||
return None
|
|
||||||
user = token.user
|
|
||||||
if user.type != UserTypes.INTERNAL_SERVICE_ACCOUNT:
|
|
||||||
LOGGER.warning(
|
|
||||||
"Remote-IP override: user doesn't have permission",
|
|
||||||
user=user,
|
|
||||||
fake_ip=fake_ip,
|
|
||||||
)
|
|
||||||
return None
|
|
||||||
# Update sentry scope to include correct IP
|
|
||||||
user = Hub.current.scope._user
|
|
||||||
if not user:
|
|
||||||
user = {}
|
|
||||||
user["ip_address"] = fake_ip
|
|
||||||
Hub.current.scope.set_user(user)
|
|
||||||
return fake_ip
|
|
||||||
|
|
||||||
|
|
||||||
def get_client_ip(request: Optional[HttpRequest]) -> str:
|
|
||||||
"""Attempt to get the client's IP by checking common HTTP Headers.
|
|
||||||
Returns none if no IP Could be found"""
|
|
||||||
if not request:
|
|
||||||
return DEFAULT_IP
|
|
||||||
override = _get_outpost_override_ip(request)
|
|
||||||
if override:
|
|
||||||
return override
|
|
||||||
return _get_client_ip_from_meta(request.META)
|
|
||||||
|
|
||||||
|
|
||||||
def authentik_user_agent() -> str:
|
def authentik_user_agent() -> str:
|
||||||
"""Get a common user agent"""
|
"""Get a common user agent"""
|
||||||
return f"authentik@{get_full_version()}"
|
return f"authentik@{get_full_version()}"
|
||||||
|
|
|
@ -9,13 +9,13 @@ from rest_framework.fields import BooleanField, CharField, DateTimeField
|
||||||
from rest_framework.relations import PrimaryKeyRelatedField
|
from rest_framework.relations import PrimaryKeyRelatedField
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.serializers import JSONField, ModelSerializer, ValidationError
|
from rest_framework.serializers import ModelSerializer, ValidationError
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
from authentik import get_build_hash
|
from authentik import get_build_hash
|
||||||
from authentik.core.api.providers import ProviderSerializer
|
from authentik.core.api.providers import ProviderSerializer
|
||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.core.api.utils import PassiveSerializer, is_dict
|
from authentik.core.api.utils import JSONDictField, PassiveSerializer
|
||||||
from authentik.core.models import Provider
|
from authentik.core.models import Provider
|
||||||
from authentik.outposts.api.service_connections import ServiceConnectionSerializer
|
from authentik.outposts.api.service_connections import ServiceConnectionSerializer
|
||||||
from authentik.outposts.apps import MANAGED_OUTPOST
|
from authentik.outposts.apps import MANAGED_OUTPOST
|
||||||
|
@ -34,7 +34,7 @@ from authentik.providers.radius.models import RadiusProvider
|
||||||
class OutpostSerializer(ModelSerializer):
|
class OutpostSerializer(ModelSerializer):
|
||||||
"""Outpost Serializer"""
|
"""Outpost Serializer"""
|
||||||
|
|
||||||
config = JSONField(validators=[is_dict], source="_config")
|
config = JSONDictField(source="_config")
|
||||||
# Need to set allow_empty=True for the embedded outpost with no providers
|
# Need to set allow_empty=True for the embedded outpost with no providers
|
||||||
# is checked for other providers in the API Viewset
|
# is checked for other providers in the API Viewset
|
||||||
providers = PrimaryKeyRelatedField(
|
providers = PrimaryKeyRelatedField(
|
||||||
|
@ -95,7 +95,7 @@ class OutpostSerializer(ModelSerializer):
|
||||||
class OutpostDefaultConfigSerializer(PassiveSerializer):
|
class OutpostDefaultConfigSerializer(PassiveSerializer):
|
||||||
"""Global default outpost config"""
|
"""Global default outpost config"""
|
||||||
|
|
||||||
config = JSONField(read_only=True)
|
config = JSONDictField(read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class OutpostHealthSerializer(PassiveSerializer):
|
class OutpostHealthSerializer(PassiveSerializer):
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
"""Serializer for policy execution"""
|
"""Serializer for policy execution"""
|
||||||
from rest_framework.fields import BooleanField, CharField, DictField, JSONField, ListField
|
from rest_framework.fields import BooleanField, CharField, DictField, ListField
|
||||||
from rest_framework.relations import PrimaryKeyRelatedField
|
from rest_framework.relations import PrimaryKeyRelatedField
|
||||||
|
|
||||||
from authentik.core.api.utils import PassiveSerializer, is_dict
|
from authentik.core.api.utils import JSONDictField, PassiveSerializer
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ class PolicyTestSerializer(PassiveSerializer):
|
||||||
"""Test policy execution for a user with context"""
|
"""Test policy execution for a user with context"""
|
||||||
|
|
||||||
user = PrimaryKeyRelatedField(queryset=User.objects.all())
|
user = PrimaryKeyRelatedField(queryset=User.objects.all())
|
||||||
context = JSONField(required=False, validators=[is_dict])
|
context = JSONDictField(required=False)
|
||||||
|
|
||||||
|
|
||||||
class PolicyTestResultSerializer(PassiveSerializer):
|
class PolicyTestResultSerializer(PassiveSerializer):
|
||||||
|
|
|
@ -7,9 +7,9 @@ from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_SSO
|
from authentik.flows.planner import PLAN_CONTEXT_SSO
|
||||||
from authentik.lib.expression.evaluator import BaseEvaluator
|
from authentik.lib.expression.evaluator import BaseEvaluator
|
||||||
from authentik.lib.utils.http import get_client_ip
|
|
||||||
from authentik.policies.exceptions import PolicyException
|
from authentik.policies.exceptions import PolicyException
|
||||||
from authentik.policies.types import PolicyRequest, PolicyResult
|
from authentik.policies.types import PolicyRequest, PolicyResult
|
||||||
|
from authentik.root.middleware import ClientIPMiddleware
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -49,7 +49,7 @@ class PolicyEvaluator(BaseEvaluator):
|
||||||
"""Update context based on http request"""
|
"""Update context based on http request"""
|
||||||
# update website/docs/expressions/_objects.md
|
# update website/docs/expressions/_objects.md
|
||||||
# update website/docs/expressions/_functions.md
|
# update website/docs/expressions/_functions.md
|
||||||
self._context["ak_client_ip"] = ip_address(get_client_ip(request))
|
self._context["ak_client_ip"] = ip_address(ClientIPMiddleware.get_client_ip(request))
|
||||||
self._context["http_request"] = request
|
self._context["http_request"] = request
|
||||||
|
|
||||||
def handle_error(self, exc: Exception, expression_source: str):
|
def handle_error(self, exc: Exception, expression_source: str):
|
||||||
|
|
|
@ -13,9 +13,9 @@ from structlog import get_logger
|
||||||
from authentik.core.models import ExpiringModel
|
from authentik.core.models import ExpiringModel
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.lib.models import SerializerModel
|
from authentik.lib.models import SerializerModel
|
||||||
from authentik.lib.utils.http import get_client_ip
|
|
||||||
from authentik.policies.models import Policy
|
from authentik.policies.models import Policy
|
||||||
from authentik.policies.types import PolicyRequest, PolicyResult
|
from authentik.policies.types import PolicyRequest, PolicyResult
|
||||||
|
from authentik.root.middleware import ClientIPMiddleware
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
CACHE_KEY_PREFIX = "goauthentik.io/policies/reputation/scores/"
|
CACHE_KEY_PREFIX = "goauthentik.io/policies/reputation/scores/"
|
||||||
|
@ -44,7 +44,7 @@ class ReputationPolicy(Policy):
|
||||||
return "ak-policy-reputation-form"
|
return "ak-policy-reputation-form"
|
||||||
|
|
||||||
def passes(self, request: PolicyRequest) -> PolicyResult:
|
def passes(self, request: PolicyRequest) -> PolicyResult:
|
||||||
remote_ip = get_client_ip(request.http_request)
|
remote_ip = ClientIPMiddleware.get_client_ip(request.http_request)
|
||||||
query = Q()
|
query = Q()
|
||||||
if self.check_ip:
|
if self.check_ip:
|
||||||
query |= Q(ip=remote_ip)
|
query |= Q(ip=remote_ip)
|
||||||
|
|
|
@ -7,9 +7,9 @@ from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.core.signals import login_failed
|
from authentik.core.signals import login_failed
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.lib.utils.http import get_client_ip
|
|
||||||
from authentik.policies.reputation.models import CACHE_KEY_PREFIX
|
from authentik.policies.reputation.models import CACHE_KEY_PREFIX
|
||||||
from authentik.policies.reputation.tasks import save_reputation
|
from authentik.policies.reputation.tasks import save_reputation
|
||||||
|
from authentik.root.middleware import ClientIPMiddleware
|
||||||
from authentik.stages.identification.signals import identification_failed
|
from authentik.stages.identification.signals import identification_failed
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
@ -18,7 +18,7 @@ CACHE_TIMEOUT = CONFIG.get_int("cache.timeout_reputation")
|
||||||
|
|
||||||
def update_score(request: HttpRequest, identifier: str, amount: int):
|
def update_score(request: HttpRequest, identifier: str, amount: int):
|
||||||
"""Update score for IP and User"""
|
"""Update score for IP and User"""
|
||||||
remote_ip = get_client_ip(request)
|
remote_ip = ClientIPMiddleware.get_client_ip(request)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# We only update the cache here, as its faster than writing to the DB
|
# We only update the cache here, as its faster than writing to the DB
|
||||||
|
|
|
@ -9,7 +9,6 @@ from django.http import HttpRequest
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.events.geo import GEOIP_READER
|
from authentik.events.geo import GEOIP_READER
|
||||||
from authentik.lib.utils.http import get_client_ip
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
|
@ -38,10 +37,12 @@ class PolicyRequest:
|
||||||
|
|
||||||
def set_http_request(self, request: HttpRequest): # pragma: no cover
|
def set_http_request(self, request: HttpRequest): # pragma: no cover
|
||||||
"""Load data from HTTP request, including geoip when enabled"""
|
"""Load data from HTTP request, including geoip when enabled"""
|
||||||
|
from authentik.root.middleware import ClientIPMiddleware
|
||||||
|
|
||||||
self.http_request = request
|
self.http_request = request
|
||||||
if not GEOIP_READER.enabled:
|
if not GEOIP_READER.enabled:
|
||||||
return
|
return
|
||||||
client_ip = get_client_ip(request)
|
client_ip = ClientIPMiddleware.get_client_ip(request)
|
||||||
if not client_ip:
|
if not client_ip:
|
||||||
return
|
return
|
||||||
self.context["geoip"] = GEOIP_READER.city(client_ip)
|
self.context["geoip"] = GEOIP_READER.city(client_ip)
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
"""SCIM constants"""
|
"""SCIM constants"""
|
||||||
PAGE_SIZE = 100
|
PAGE_SIZE = 100
|
||||||
|
PAGE_TIMEOUT = 60 * 60 * 0.5 # Half an hour
|
||||||
|
|
|
@ -12,7 +12,7 @@ from structlog.stdlib import get_logger
|
||||||
from authentik.core.models import Group, User
|
from authentik.core.models import Group, User
|
||||||
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
|
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
|
||||||
from authentik.lib.utils.reflection import path_to_class
|
from authentik.lib.utils.reflection import path_to_class
|
||||||
from authentik.providers.scim.clients import PAGE_SIZE
|
from authentik.providers.scim.clients import PAGE_SIZE, PAGE_TIMEOUT
|
||||||
from authentik.providers.scim.clients.base import SCIMClient
|
from authentik.providers.scim.clients.base import SCIMClient
|
||||||
from authentik.providers.scim.clients.exceptions import SCIMRequestException, StopSync
|
from authentik.providers.scim.clients.exceptions import SCIMRequestException, StopSync
|
||||||
from authentik.providers.scim.clients.group import SCIMGroupClient
|
from authentik.providers.scim.clients.group import SCIMGroupClient
|
||||||
|
@ -53,6 +53,9 @@ def scim_sync(self: MonitoredTask, provider_pk: int) -> None:
|
||||||
LOGGER.debug("Starting SCIM sync")
|
LOGGER.debug("Starting SCIM sync")
|
||||||
users_paginator = Paginator(provider.get_user_qs(), PAGE_SIZE)
|
users_paginator = Paginator(provider.get_user_qs(), PAGE_SIZE)
|
||||||
groups_paginator = Paginator(provider.get_group_qs(), PAGE_SIZE)
|
groups_paginator = Paginator(provider.get_group_qs(), PAGE_SIZE)
|
||||||
|
self.soft_time_limit = self.time_limit = (
|
||||||
|
users_paginator.count + groups_paginator.count
|
||||||
|
) * PAGE_TIMEOUT
|
||||||
with allow_join_result():
|
with allow_join_result():
|
||||||
try:
|
try:
|
||||||
for page in users_paginator.page_range:
|
for page in users_paginator.page_range:
|
||||||
|
@ -69,7 +72,10 @@ def scim_sync(self: MonitoredTask, provider_pk: int) -> None:
|
||||||
self.set_status(result)
|
self.set_status(result)
|
||||||
|
|
||||||
|
|
||||||
@CELERY_APP.task()
|
@CELERY_APP.task(
|
||||||
|
soft_time_limit=PAGE_TIMEOUT,
|
||||||
|
task_time_limit=PAGE_TIMEOUT,
|
||||||
|
)
|
||||||
def scim_sync_users(page: int, provider_pk: int):
|
def scim_sync_users(page: int, provider_pk: int):
|
||||||
"""Sync single or multiple users to SCIM"""
|
"""Sync single or multiple users to SCIM"""
|
||||||
messages = []
|
messages = []
|
||||||
|
|
|
@ -61,7 +61,7 @@ class SCIMUserTests(TestCase):
|
||||||
uid = generate_id()
|
uid = generate_id()
|
||||||
user = User.objects.create(
|
user = User.objects.create(
|
||||||
username=uid,
|
username=uid,
|
||||||
name=uid,
|
name=f"{uid} {uid}",
|
||||||
email=f"{uid}@goauthentik.io",
|
email=f"{uid}@goauthentik.io",
|
||||||
)
|
)
|
||||||
self.assertEqual(mock.call_count, 2)
|
self.assertEqual(mock.call_count, 2)
|
||||||
|
@ -81,11 +81,11 @@ class SCIMUserTests(TestCase):
|
||||||
],
|
],
|
||||||
"externalId": user.uid,
|
"externalId": user.uid,
|
||||||
"name": {
|
"name": {
|
||||||
"familyName": "",
|
"familyName": uid,
|
||||||
"formatted": uid,
|
"formatted": f"{uid} {uid}",
|
||||||
"givenName": uid,
|
"givenName": uid,
|
||||||
},
|
},
|
||||||
"displayName": uid,
|
"displayName": f"{uid} {uid}",
|
||||||
"userName": uid,
|
"userName": uid,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -114,7 +114,7 @@ class SCIMUserTests(TestCase):
|
||||||
uid = generate_id()
|
uid = generate_id()
|
||||||
user = User.objects.create(
|
user = User.objects.create(
|
||||||
username=uid,
|
username=uid,
|
||||||
name=uid,
|
name=f"{uid} {uid}",
|
||||||
email=f"{uid}@goauthentik.io",
|
email=f"{uid}@goauthentik.io",
|
||||||
)
|
)
|
||||||
self.assertEqual(mock.call_count, 2)
|
self.assertEqual(mock.call_count, 2)
|
||||||
|
@ -135,11 +135,11 @@ class SCIMUserTests(TestCase):
|
||||||
"value": f"{uid}@goauthentik.io",
|
"value": f"{uid}@goauthentik.io",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"displayName": uid,
|
"displayName": f"{uid} {uid}",
|
||||||
"externalId": user.uid,
|
"externalId": user.uid,
|
||||||
"name": {
|
"name": {
|
||||||
"familyName": "",
|
"familyName": uid,
|
||||||
"formatted": uid,
|
"formatted": f"{uid} {uid}",
|
||||||
"givenName": uid,
|
"givenName": uid,
|
||||||
},
|
},
|
||||||
"userName": uid,
|
"userName": uid,
|
||||||
|
@ -170,7 +170,7 @@ class SCIMUserTests(TestCase):
|
||||||
uid = generate_id()
|
uid = generate_id()
|
||||||
user = User.objects.create(
|
user = User.objects.create(
|
||||||
username=uid,
|
username=uid,
|
||||||
name=uid,
|
name=f"{uid} {uid}",
|
||||||
email=f"{uid}@goauthentik.io",
|
email=f"{uid}@goauthentik.io",
|
||||||
)
|
)
|
||||||
self.assertEqual(mock.call_count, 2)
|
self.assertEqual(mock.call_count, 2)
|
||||||
|
@ -190,11 +190,11 @@ class SCIMUserTests(TestCase):
|
||||||
],
|
],
|
||||||
"externalId": user.uid,
|
"externalId": user.uid,
|
||||||
"name": {
|
"name": {
|
||||||
"familyName": "",
|
"familyName": uid,
|
||||||
"formatted": uid,
|
"formatted": f"{uid} {uid}",
|
||||||
"givenName": uid,
|
"givenName": uid,
|
||||||
},
|
},
|
||||||
"displayName": uid,
|
"displayName": f"{uid} {uid}",
|
||||||
"userName": uid,
|
"userName": uid,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -234,7 +234,7 @@ class SCIMUserTests(TestCase):
|
||||||
)
|
)
|
||||||
user = User.objects.create(
|
user = User.objects.create(
|
||||||
username=uid,
|
username=uid,
|
||||||
name=uid,
|
name=f"{uid} {uid}",
|
||||||
email=f"{uid}@goauthentik.io",
|
email=f"{uid}@goauthentik.io",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -258,11 +258,11 @@ class SCIMUserTests(TestCase):
|
||||||
],
|
],
|
||||||
"externalId": user.uid,
|
"externalId": user.uid,
|
||||||
"name": {
|
"name": {
|
||||||
"familyName": "",
|
"familyName": uid,
|
||||||
"formatted": uid,
|
"formatted": f"{uid} {uid}",
|
||||||
"givenName": uid,
|
"givenName": uid,
|
||||||
},
|
},
|
||||||
"displayName": uid,
|
"displayName": f"{uid} {uid}",
|
||||||
"userName": uid,
|
"userName": uid,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
from hashlib import sha512
|
from hashlib import sha512
|
||||||
from time import time
|
from time import time
|
||||||
from timeit import default_timer
|
from timeit import default_timer
|
||||||
from typing import Callable
|
from typing import Any, Callable, Optional
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.sessions.backends.base import UpdateError
|
from django.contrib.sessions.backends.base import UpdateError
|
||||||
|
@ -15,9 +15,10 @@ from django.middleware.csrf import CsrfViewMiddleware as UpstreamCsrfViewMiddlew
|
||||||
from django.utils.cache import patch_vary_headers
|
from django.utils.cache import patch_vary_headers
|
||||||
from django.utils.http import http_date
|
from django.utils.http import http_date
|
||||||
from jwt import PyJWTError, decode, encode
|
from jwt import PyJWTError, decode, encode
|
||||||
|
from sentry_sdk.hub import Hub
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.lib.utils.http import get_client_ip
|
from authentik.core.models import Token, TokenIntents, User, UserTypes
|
||||||
|
|
||||||
LOGGER = get_logger("authentik.asgi")
|
LOGGER = get_logger("authentik.asgi")
|
||||||
ACR_AUTHENTIK_SESSION = "goauthentik.io/core/default"
|
ACR_AUTHENTIK_SESSION = "goauthentik.io/core/default"
|
||||||
|
@ -156,6 +157,111 @@ class CsrfViewMiddleware(UpstreamCsrfViewMiddleware):
|
||||||
patch_vary_headers(response, ("Cookie",))
|
patch_vary_headers(response, ("Cookie",))
|
||||||
|
|
||||||
|
|
||||||
|
class ClientIPMiddleware:
|
||||||
|
"""Set a "known-good" client IP on the request, by default based off of x-forwarded-for
|
||||||
|
which is set by the go proxy, but also allowing the remote IP to be overridden by an outpost
|
||||||
|
for protocols like LDAP"""
|
||||||
|
|
||||||
|
get_response: Callable[[HttpRequest], HttpResponse]
|
||||||
|
outpost_remote_ip_header = "HTTP_X_AUTHENTIK_REMOTE_IP"
|
||||||
|
outpost_token_header = "HTTP_X_AUTHENTIK_OUTPOST_TOKEN" # nosec
|
||||||
|
default_ip = "255.255.255.255"
|
||||||
|
|
||||||
|
request_attr_client_ip = "client_ip"
|
||||||
|
request_attr_outpost_user = "outpost_user"
|
||||||
|
|
||||||
|
def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]):
|
||||||
|
self.get_response = get_response
|
||||||
|
|
||||||
|
def _get_client_ip_from_meta(self, meta: dict[str, Any]) -> str:
|
||||||
|
"""Attempt to get the client's IP by checking common HTTP Headers.
|
||||||
|
Returns none if no IP Could be found
|
||||||
|
|
||||||
|
No additional validation is done here as requests are expected to only arrive here
|
||||||
|
via the go proxy, which deals with validating these headers for us"""
|
||||||
|
headers = (
|
||||||
|
"HTTP_X_FORWARDED_FOR",
|
||||||
|
"REMOTE_ADDR",
|
||||||
|
)
|
||||||
|
for _header in headers:
|
||||||
|
if _header in meta:
|
||||||
|
ips: list[str] = meta.get(_header).split(",")
|
||||||
|
return ips[0].strip()
|
||||||
|
return self.default_ip
|
||||||
|
|
||||||
|
# FIXME: this should probably not be in `root` but rather in a middleware in `outposts`
|
||||||
|
# but for now it's fine
|
||||||
|
def _get_outpost_override_ip(self, request: HttpRequest) -> Optional[str]:
|
||||||
|
"""Get the actual remote IP when set by an outpost. Only
|
||||||
|
allowed when the request is authenticated, by an outpost internal service account"""
|
||||||
|
if (
|
||||||
|
self.outpost_remote_ip_header not in request.META
|
||||||
|
or self.outpost_token_header not in request.META
|
||||||
|
):
|
||||||
|
return None
|
||||||
|
delegated_ip = request.META[self.outpost_remote_ip_header]
|
||||||
|
token = (
|
||||||
|
Token.filter_not_expired(
|
||||||
|
key=request.META.get(self.outpost_token_header), intent=TokenIntents.INTENT_API
|
||||||
|
)
|
||||||
|
.select_related("user")
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
if not token:
|
||||||
|
LOGGER.warning("Attempted remote-ip override without token", delegated_ip=delegated_ip)
|
||||||
|
return None
|
||||||
|
user: User = token.user
|
||||||
|
if user.type != UserTypes.INTERNAL_SERVICE_ACCOUNT:
|
||||||
|
LOGGER.warning(
|
||||||
|
"Remote-IP override: user doesn't have permission",
|
||||||
|
user=user,
|
||||||
|
delegated_ip=delegated_ip,
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
# Update sentry scope to include correct IP
|
||||||
|
user = Hub.current.scope._user
|
||||||
|
if not user:
|
||||||
|
user = {}
|
||||||
|
user["ip_address"] = delegated_ip
|
||||||
|
Hub.current.scope.set_user(user)
|
||||||
|
# Set the outpost service account on the request
|
||||||
|
setattr(request, self.request_attr_outpost_user, user)
|
||||||
|
return delegated_ip
|
||||||
|
|
||||||
|
def _get_client_ip(self, request: Optional[HttpRequest]) -> str:
|
||||||
|
"""Attempt to get the client's IP by checking common HTTP Headers.
|
||||||
|
Returns none if no IP Could be found"""
|
||||||
|
if not request:
|
||||||
|
return self.default_ip
|
||||||
|
override = self._get_outpost_override_ip(request)
|
||||||
|
if override:
|
||||||
|
return override
|
||||||
|
return self._get_client_ip_from_meta(request.META)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_outpost_user(request: HttpRequest) -> Optional[User]:
|
||||||
|
"""Get outpost user that authenticated this request"""
|
||||||
|
return getattr(request, ClientIPMiddleware.request_attr_outpost_user, None)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_client_ip(request: HttpRequest) -> str:
|
||||||
|
"""Get correct client IP, including any overrides from outposts that
|
||||||
|
have the permission to do so"""
|
||||||
|
if request and not hasattr(request, ClientIPMiddleware.request_attr_client_ip):
|
||||||
|
ClientIPMiddleware(lambda request: request).set_ip(request)
|
||||||
|
return getattr(
|
||||||
|
request, ClientIPMiddleware.request_attr_client_ip, ClientIPMiddleware.default_ip
|
||||||
|
)
|
||||||
|
|
||||||
|
def set_ip(self, request: HttpRequest):
|
||||||
|
"""Set the IP"""
|
||||||
|
setattr(request, self.request_attr_client_ip, self._get_client_ip(request))
|
||||||
|
|
||||||
|
def __call__(self, request: HttpRequest) -> HttpResponse:
|
||||||
|
self.set_ip(request)
|
||||||
|
return self.get_response(request)
|
||||||
|
|
||||||
|
|
||||||
class ChannelsLoggingMiddleware:
|
class ChannelsLoggingMiddleware:
|
||||||
"""Logging middleware for channels"""
|
"""Logging middleware for channels"""
|
||||||
|
|
||||||
|
@ -201,7 +307,7 @@ class LoggingMiddleware:
|
||||||
"""Log request"""
|
"""Log request"""
|
||||||
LOGGER.info(
|
LOGGER.info(
|
||||||
request.get_full_path(),
|
request.get_full_path(),
|
||||||
remote=get_client_ip(request),
|
remote=ClientIPMiddleware.get_client_ip(request),
|
||||||
method=request.method,
|
method=request.method,
|
||||||
scheme=request.scheme,
|
scheme=request.scheme,
|
||||||
status=status_code,
|
status=status_code,
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
"""
|
||||||
|
Module for abstract serializer/unserializer base classes.
|
||||||
|
"""
|
||||||
|
import pickle # nosec
|
||||||
|
|
||||||
|
|
||||||
|
class PickleSerializer:
|
||||||
|
"""
|
||||||
|
Simple wrapper around pickle to be used in signing.dumps()/loads() and
|
||||||
|
cache backends.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, protocol=None):
|
||||||
|
self.protocol = pickle.HIGHEST_PROTOCOL if protocol is None else protocol
|
||||||
|
|
||||||
|
def dumps(self, obj):
|
||||||
|
"""Pickle data to be stored in redis"""
|
||||||
|
return pickle.dumps(obj, self.protocol)
|
||||||
|
|
||||||
|
def loads(self, data):
|
||||||
|
"""Unpickle data to be loaded from redis"""
|
||||||
|
return pickle.loads(data) # nosec
|
|
@ -151,6 +151,7 @@ SPECTACULAR_SETTINGS = {
|
||||||
"EventActions": "authentik.events.models.EventAction",
|
"EventActions": "authentik.events.models.EventAction",
|
||||||
"ChallengeChoices": "authentik.flows.challenge.ChallengeTypes",
|
"ChallengeChoices": "authentik.flows.challenge.ChallengeTypes",
|
||||||
"FlowDesignationEnum": "authentik.flows.models.FlowDesignation",
|
"FlowDesignationEnum": "authentik.flows.models.FlowDesignation",
|
||||||
|
"FlowLayoutEnum": "authentik.flows.models.FlowLayout",
|
||||||
"PolicyEngineMode": "authentik.policies.models.PolicyEngineMode",
|
"PolicyEngineMode": "authentik.policies.models.PolicyEngineMode",
|
||||||
"ProxyMode": "authentik.providers.proxy.models.ProxyMode",
|
"ProxyMode": "authentik.providers.proxy.models.ProxyMode",
|
||||||
"PromptTypeEnum": "authentik.stages.prompt.models.FieldTypes",
|
"PromptTypeEnum": "authentik.stages.prompt.models.FieldTypes",
|
||||||
|
@ -219,7 +220,7 @@ DJANGO_REDIS_SCAN_ITERSIZE = 1000
|
||||||
DJANGO_REDIS_IGNORE_EXCEPTIONS = True
|
DJANGO_REDIS_IGNORE_EXCEPTIONS = True
|
||||||
DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS = True
|
DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS = True
|
||||||
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
|
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
|
||||||
SESSION_SERIALIZER = "django.contrib.sessions.serializers.PickleSerializer"
|
SESSION_SERIALIZER = "authentik.root.sessions.pickle.PickleSerializer"
|
||||||
SESSION_CACHE_ALIAS = "default"
|
SESSION_CACHE_ALIAS = "default"
|
||||||
# Configured via custom SessionMiddleware
|
# Configured via custom SessionMiddleware
|
||||||
# SESSION_COOKIE_SAMESITE = "None"
|
# SESSION_COOKIE_SAMESITE = "None"
|
||||||
|
@ -232,7 +233,7 @@ MIDDLEWARE = [
|
||||||
"django_tenants.middleware.default.DefaultTenantMiddleware",
|
"django_tenants.middleware.default.DefaultTenantMiddleware",
|
||||||
"authentik.root.middleware.LoggingMiddleware",
|
"authentik.root.middleware.LoggingMiddleware",
|
||||||
"django_prometheus.middleware.PrometheusBeforeMiddleware",
|
"django_prometheus.middleware.PrometheusBeforeMiddleware",
|
||||||
"authentik.brands.middleware.BrandMiddleware",
|
"authentik.root.middleware.ClientIPMiddleware",
|
||||||
"authentik.root.middleware.SessionMiddleware",
|
"authentik.root.middleware.SessionMiddleware",
|
||||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
"authentik.core.middleware.RequestIDMiddleware",
|
"authentik.core.middleware.RequestIDMiddleware",
|
||||||
|
@ -295,6 +296,9 @@ DATABASES = {
|
||||||
"SSLROOTCERT": CONFIG.get("postgresql.sslrootcert"),
|
"SSLROOTCERT": CONFIG.get("postgresql.sslrootcert"),
|
||||||
"SSLCERT": CONFIG.get("postgresql.sslcert"),
|
"SSLCERT": CONFIG.get("postgresql.sslcert"),
|
||||||
"SSLKEY": CONFIG.get("postgresql.sslkey"),
|
"SSLKEY": CONFIG.get("postgresql.sslkey"),
|
||||||
|
"TEST": {
|
||||||
|
"NAME": CONFIG.get("postgresql.test.name"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,8 @@ from authentik.stages.authenticator.util import hex_validator, random_hex
|
||||||
class TOTPDigits(models.TextChoices):
|
class TOTPDigits(models.TextChoices):
|
||||||
"""OTP Time Digits"""
|
"""OTP Time Digits"""
|
||||||
|
|
||||||
SIX = 6, _("6 digits, widely compatible")
|
SIX = "6", _("6 digits, widely compatible")
|
||||||
EIGHT = 8, _("8 digits, not compatible with apps like Google Authenticator")
|
EIGHT = "8", _("8 digits, not compatible with apps like Google Authenticator")
|
||||||
|
|
||||||
|
|
||||||
class AuthenticatorTOTPStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
class AuthenticatorTOTPStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
||||||
|
|
|
@ -7,7 +7,7 @@ from django.http.response import Http404
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.utils.translation import gettext as __
|
from django.utils.translation import gettext as __
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from rest_framework.fields import CharField, JSONField
|
from rest_framework.fields import CharField
|
||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
from webauthn.authentication.generate_authentication_options import generate_authentication_options
|
from webauthn.authentication.generate_authentication_options import generate_authentication_options
|
||||||
|
@ -16,13 +16,13 @@ from webauthn.helpers.base64url_to_bytes import base64url_to_bytes
|
||||||
from webauthn.helpers.exceptions import InvalidAuthenticationResponse
|
from webauthn.helpers.exceptions import InvalidAuthenticationResponse
|
||||||
from webauthn.helpers.structs import AuthenticationCredential
|
from webauthn.helpers.structs import AuthenticationCredential
|
||||||
|
|
||||||
from authentik.core.api.utils import PassiveSerializer
|
from authentik.core.api.utils import JSONDictField, PassiveSerializer
|
||||||
from authentik.core.models import Application, User
|
from authentik.core.models import Application, User
|
||||||
from authentik.core.signals import login_failed
|
from authentik.core.signals import login_failed
|
||||||
from authentik.events.models import Event, EventAction
|
from authentik.events.models import Event, EventAction
|
||||||
from authentik.flows.stage import StageView
|
from authentik.flows.stage import StageView
|
||||||
from authentik.flows.views.executor import SESSION_KEY_APPLICATION_PRE
|
from authentik.flows.views.executor import SESSION_KEY_APPLICATION_PRE
|
||||||
from authentik.lib.utils.http import get_client_ip
|
from authentik.root.middleware import ClientIPMiddleware
|
||||||
from authentik.stages.authenticator import match_token
|
from authentik.stages.authenticator import match_token
|
||||||
from authentik.stages.authenticator.models import Device
|
from authentik.stages.authenticator.models import Device
|
||||||
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
|
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
|
||||||
|
@ -40,7 +40,7 @@ class DeviceChallenge(PassiveSerializer):
|
||||||
|
|
||||||
device_class = CharField()
|
device_class = CharField()
|
||||||
device_uid = CharField()
|
device_uid = CharField()
|
||||||
challenge = JSONField()
|
challenge = JSONDictField()
|
||||||
|
|
||||||
|
|
||||||
def get_challenge_for_device(
|
def get_challenge_for_device(
|
||||||
|
@ -197,7 +197,7 @@ def validate_challenge_duo(device_pk: int, stage_view: StageView, user: User) ->
|
||||||
response = stage.auth_client().auth(
|
response = stage.auth_client().auth(
|
||||||
"auto",
|
"auto",
|
||||||
user_id=device.duo_user_id,
|
user_id=device.duo_user_id,
|
||||||
ipaddr=get_client_ip(stage_view.request),
|
ipaddr=ClientIPMiddleware.get_client_ip(stage_view.request),
|
||||||
type=__(
|
type=__(
|
||||||
"%(brand_name)s Login request"
|
"%(brand_name)s Login request"
|
||||||
% {
|
% {
|
||||||
|
|
|
@ -6,10 +6,10 @@ from typing import Optional
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from jwt import PyJWTError, decode, encode
|
from jwt import PyJWTError, decode, encode
|
||||||
from rest_framework.fields import CharField, IntegerField, JSONField, ListField, UUIDField
|
from rest_framework.fields import CharField, IntegerField, ListField, UUIDField
|
||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
|
|
||||||
from authentik.core.api.utils import PassiveSerializer
|
from authentik.core.api.utils import JSONDictField, PassiveSerializer
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.events.models import Event, EventAction
|
from authentik.events.models import Event, EventAction
|
||||||
from authentik.flows.challenge import ChallengeResponse, ChallengeTypes, WithUserInfoChallenge
|
from authentik.flows.challenge import ChallengeResponse, ChallengeTypes, WithUserInfoChallenge
|
||||||
|
@ -68,7 +68,7 @@ class AuthenticatorValidationChallengeResponse(ChallengeResponse):
|
||||||
selected_stage = CharField(required=False)
|
selected_stage = CharField(required=False)
|
||||||
|
|
||||||
code = CharField(required=False)
|
code = CharField(required=False)
|
||||||
webauthn = JSONField(required=False)
|
webauthn = JSONDictField(required=False)
|
||||||
duo = IntegerField(required=False)
|
duo = IntegerField(required=False)
|
||||||
component = CharField(default="ak-stage-authenticator-validate")
|
component = CharField(default="ak-stage-authenticator-validate")
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""WebAuthn stage"""
|
"""WebAuthn stage"""
|
||||||
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 rest_framework.fields import CharField, JSONField
|
from rest_framework.fields import CharField
|
||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
from webauthn.helpers.bytes_to_base64url import bytes_to_base64url
|
from webauthn.helpers.bytes_to_base64url import bytes_to_base64url
|
||||||
from webauthn.helpers.exceptions import InvalidRegistrationResponse
|
from webauthn.helpers.exceptions import InvalidRegistrationResponse
|
||||||
|
@ -16,6 +16,7 @@ from webauthn.registration.verify_registration_response import (
|
||||||
verify_registration_response,
|
verify_registration_response,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from authentik.core.api.utils import JSONDictField
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.flows.challenge import (
|
from authentik.flows.challenge import (
|
||||||
Challenge,
|
Challenge,
|
||||||
|
@ -33,14 +34,14 @@ SESSION_KEY_WEBAUTHN_CHALLENGE = "authentik/stages/authenticator_webauthn/challe
|
||||||
class AuthenticatorWebAuthnChallenge(WithUserInfoChallenge):
|
class AuthenticatorWebAuthnChallenge(WithUserInfoChallenge):
|
||||||
"""WebAuthn Challenge"""
|
"""WebAuthn Challenge"""
|
||||||
|
|
||||||
registration = JSONField()
|
registration = JSONDictField()
|
||||||
component = CharField(default="ak-stage-authenticator-webauthn")
|
component = CharField(default="ak-stage-authenticator-webauthn")
|
||||||
|
|
||||||
|
|
||||||
class AuthenticatorWebAuthnChallengeResponse(ChallengeResponse):
|
class AuthenticatorWebAuthnChallengeResponse(ChallengeResponse):
|
||||||
"""WebAuthn Challenge response"""
|
"""WebAuthn Challenge response"""
|
||||||
|
|
||||||
response = JSONField()
|
response = JSONDictField()
|
||||||
component = CharField(default="ak-stage-authenticator-webauthn")
|
component = CharField(default="ak-stage-authenticator-webauthn")
|
||||||
|
|
||||||
request: HttpRequest
|
request: HttpRequest
|
||||||
|
|
|
@ -12,7 +12,8 @@ from authentik.flows.challenge import (
|
||||||
WithUserInfoChallenge,
|
WithUserInfoChallenge,
|
||||||
)
|
)
|
||||||
from authentik.flows.stage import ChallengeStageView
|
from authentik.flows.stage import ChallengeStageView
|
||||||
from authentik.lib.utils.http import get_client_ip, get_http_session
|
from authentik.lib.utils.http import get_http_session
|
||||||
|
from authentik.root.middleware import ClientIPMiddleware
|
||||||
from authentik.stages.captcha.models import CaptchaStage
|
from authentik.stages.captcha.models import CaptchaStage
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,7 +43,7 @@ class CaptchaChallengeResponse(ChallengeResponse):
|
||||||
data={
|
data={
|
||||||
"secret": stage.private_key,
|
"secret": stage.private_key,
|
||||||
"response": token,
|
"response": token,
|
||||||
"remoteip": get_client_ip(self.stage.request),
|
"remoteip": ClientIPMiddleware.get_client_ip(self.stage.request),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
|
|
@ -26,8 +26,8 @@ from authentik.flows.models import FlowDesignation
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
|
||||||
from authentik.flows.stage import PLAN_CONTEXT_PENDING_USER_IDENTIFIER, ChallengeStageView
|
from authentik.flows.stage import PLAN_CONTEXT_PENDING_USER_IDENTIFIER, ChallengeStageView
|
||||||
from authentik.flows.views.executor import SESSION_KEY_APPLICATION_PRE, SESSION_KEY_GET
|
from authentik.flows.views.executor import SESSION_KEY_APPLICATION_PRE, SESSION_KEY_GET
|
||||||
from authentik.lib.utils.http import get_client_ip
|
|
||||||
from authentik.lib.utils.urls import reverse_with_qs
|
from authentik.lib.utils.urls import reverse_with_qs
|
||||||
|
from authentik.root.middleware import ClientIPMiddleware
|
||||||
from authentik.sources.oauth.types.apple import AppleLoginChallenge
|
from authentik.sources.oauth.types.apple import AppleLoginChallenge
|
||||||
from authentik.sources.plex.models import PlexAuthenticationChallenge
|
from authentik.sources.plex.models import PlexAuthenticationChallenge
|
||||||
from authentik.stages.identification.models import IdentificationStage
|
from authentik.stages.identification.models import IdentificationStage
|
||||||
|
@ -103,7 +103,7 @@ class IdentificationChallengeResponse(ChallengeResponse):
|
||||||
self.stage.logger.info(
|
self.stage.logger.info(
|
||||||
"invalid_login",
|
"invalid_login",
|
||||||
identifier=uid_field,
|
identifier=uid_field,
|
||||||
client_ip=get_client_ip(self.stage.request),
|
client_ip=ClientIPMiddleware.get_client_ip(self.stage.request),
|
||||||
action="invalid_identifier",
|
action="invalid_identifier",
|
||||||
context={
|
context={
|
||||||
"stage": sanitize_item(self.stage),
|
"stage": sanitize_item(self.stage),
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
"""Invitation Stage API Views"""
|
"""Invitation Stage API Views"""
|
||||||
from django_filters.filters import BooleanFilter
|
from django_filters.filters import BooleanFilter
|
||||||
from django_filters.filterset import FilterSet
|
from django_filters.filterset import FilterSet
|
||||||
from rest_framework.fields import JSONField
|
|
||||||
from rest_framework.serializers import ModelSerializer
|
from rest_framework.serializers import ModelSerializer
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
from authentik.core.api.groups import GroupMemberSerializer
|
from authentik.core.api.groups import GroupMemberSerializer
|
||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.core.api.utils import is_dict
|
from authentik.core.api.utils import JSONDictField
|
||||||
from authentik.flows.api.flows import FlowSerializer
|
from authentik.flows.api.flows import FlowSerializer
|
||||||
from authentik.flows.api.stages import StageSerializer
|
from authentik.flows.api.stages import StageSerializer
|
||||||
from authentik.stages.invitation.models import Invitation, InvitationStage
|
from authentik.stages.invitation.models import Invitation, InvitationStage
|
||||||
|
@ -47,7 +46,7 @@ class InvitationSerializer(ModelSerializer):
|
||||||
"""Invitation Serializer"""
|
"""Invitation Serializer"""
|
||||||
|
|
||||||
created_by = GroupMemberSerializer(read_only=True)
|
created_by = GroupMemberSerializer(read_only=True)
|
||||||
fixed_data = JSONField(validators=[is_dict], required=False)
|
fixed_data = JSONDictField(required=False)
|
||||||
flow_obj = FlowSerializer(read_only=True, required=False, source="flow")
|
flow_obj = FlowSerializer(read_only=True, required=False, source="flow")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -16,8 +16,8 @@ from authentik.flows.tests import FlowTestCase
|
||||||
from authentik.flows.tests.test_executor import TO_STAGE_RESPONSE_MOCK
|
from authentik.flows.tests.test_executor import TO_STAGE_RESPONSE_MOCK
|
||||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||||
from authentik.lib.generators import generate_id
|
from authentik.lib.generators import generate_id
|
||||||
from authentik.lib.utils.http import DEFAULT_IP
|
|
||||||
from authentik.lib.utils.time import timedelta_from_string
|
from authentik.lib.utils.time import timedelta_from_string
|
||||||
|
from authentik.root.middleware import ClientIPMiddleware
|
||||||
from authentik.stages.user_login.models import UserLoginStage
|
from authentik.stages.user_login.models import UserLoginStage
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ class TestUserLoginStage(FlowTestCase):
|
||||||
other_session = AuthenticatedSession.objects.create(
|
other_session = AuthenticatedSession.objects.create(
|
||||||
user=self.user,
|
user=self.user,
|
||||||
session_key=key,
|
session_key=key,
|
||||||
last_ip=DEFAULT_IP,
|
last_ip=ClientIPMiddleware.default_ip,
|
||||||
)
|
)
|
||||||
cache.set(f"{KEY_PREFIX}{other_session.session_key}", "foo")
|
cache.set(f"{KEY_PREFIX}{other_session.session_key}", "foo")
|
||||||
|
|
||||||
|
|
|
@ -3282,7 +3282,8 @@
|
||||||
"none",
|
"none",
|
||||||
"require_authenticated",
|
"require_authenticated",
|
||||||
"require_unauthenticated",
|
"require_unauthenticated",
|
||||||
"require_superuser"
|
"require_superuser",
|
||||||
|
"require_outpost"
|
||||||
],
|
],
|
||||||
"title": "Authentication",
|
"title": "Authentication",
|
||||||
"description": "Required level of authentication and authorization to access a flow."
|
"description": "Required level of authentication and authorization to access a flow."
|
||||||
|
@ -5415,22 +5416,8 @@
|
||||||
"title": "Icon"
|
"title": "Icon"
|
||||||
},
|
},
|
||||||
"provider_type": {
|
"provider_type": {
|
||||||
"type": "string",
|
"type": [],
|
||||||
"enum": [
|
"enum": [],
|
||||||
"apple",
|
|
||||||
"azuread",
|
|
||||||
"discord",
|
|
||||||
"facebook",
|
|
||||||
"github",
|
|
||||||
"google",
|
|
||||||
"mailcow",
|
|
||||||
"openidconnect",
|
|
||||||
"okta",
|
|
||||||
"patreon",
|
|
||||||
"reddit",
|
|
||||||
"twitch",
|
|
||||||
"twitter"
|
|
||||||
],
|
|
||||||
"title": "Provider type"
|
"title": "Provider type"
|
||||||
},
|
},
|
||||||
"request_token_url": {
|
"request_token_url": {
|
||||||
|
|
|
@ -12,12 +12,14 @@ entries:
|
||||||
expression: |
|
expression: |
|
||||||
# Some implementations require givenName and familyName to be set
|
# Some implementations require givenName and familyName to be set
|
||||||
givenName, familyName = request.user.name, " "
|
givenName, familyName = request.user.name, " "
|
||||||
|
formatted = request.user.name + " "
|
||||||
# This default sets givenName to the name before the first space
|
# This default sets givenName to the name before the first space
|
||||||
# and the remainder as family name
|
# and the remainder as family name
|
||||||
# if the user's name has no space the givenName is the entire name
|
# if the user's name has no space the givenName is the entire name
|
||||||
# (this might cause issues with some SCIM implementations)
|
# (this might cause issues with some SCIM implementations)
|
||||||
if " " in request.user.name:
|
if " " in request.user.name:
|
||||||
givenName, _, familyName = request.user.name.partition(" ")
|
givenName, _, familyName = request.user.name.partition(" ")
|
||||||
|
formatted = request.user.name
|
||||||
|
|
||||||
# photos supports URLs to images, however authentik might return data URIs
|
# photos supports URLs to images, however authentik might return data URIs
|
||||||
avatar = request.user.avatar
|
avatar = request.user.avatar
|
||||||
|
@ -39,7 +41,7 @@ entries:
|
||||||
return {
|
return {
|
||||||
"userName": request.user.username,
|
"userName": request.user.username,
|
||||||
"name": {
|
"name": {
|
||||||
"formatted": request.user.name,
|
"formatted": formatted,
|
||||||
"givenName": givenName,
|
"givenName": givenName,
|
||||||
"familyName": familyName,
|
"familyName": familyName,
|
||||||
},
|
},
|
||||||
|
|
|
@ -81,7 +81,6 @@ var rootCmd = &cobra.Command{
|
||||||
for {
|
for {
|
||||||
<-ex
|
<-ex
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
8
go.mod
8
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.2
|
github.com/go-openapi/runtime v0.26.2
|
||||||
github.com/go-openapi/strfmt v0.21.9
|
github.com/go-openapi/strfmt v0.21.10
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||||
github.com/google/uuid v1.5.0
|
github.com/google/uuid v1.5.0
|
||||||
github.com/gorilla/handlers v1.5.2
|
github.com/gorilla/handlers v1.5.2
|
||||||
|
@ -27,7 +27,7 @@ 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.2
|
goauthentik.io/api/v3 v3.2023104.3
|
||||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||||
golang.org/x/oauth2 v0.15.0
|
golang.org/x/oauth2 v0.15.0
|
||||||
golang.org/x/sync v0.5.0
|
golang.org/x/sync v0.5.0
|
||||||
|
@ -49,7 +49,7 @@ require (
|
||||||
github.com/go-logr/logr v1.3.0 // indirect
|
github.com/go-logr/logr v1.3.0 // 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.4 // indirect
|
github.com/go-openapi/errors v0.21.0 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.20.0 // indirect
|
github.com/go-openapi/jsonpointer v0.20.0 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||||
github.com/go-openapi/loads v0.21.2 // indirect
|
github.com/go-openapi/loads v0.21.2 // indirect
|
||||||
|
@ -73,7 +73,7 @@ require (
|
||||||
go.opentelemetry.io/otel v1.17.0 // indirect
|
go.opentelemetry.io/otel v1.17.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.17.0 // indirect
|
go.opentelemetry.io/otel/metric v1.17.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.17.0 // indirect
|
go.opentelemetry.io/otel/trace v1.17.0 // indirect
|
||||||
golang.org/x/crypto v0.16.0 // indirect
|
golang.org/x/crypto v0.17.0 // indirect
|
||||||
golang.org/x/net v0.19.0 // indirect
|
golang.org/x/net v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.15.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
|
||||||
|
|
16
go.sum
16
go.sum
|
@ -98,8 +98,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
|
||||||
github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc=
|
github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc=
|
||||||
github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo=
|
github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo=
|
||||||
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.4 h1:unTcVm6PispJsMECE3zWgvG4xTiKda1LIR5rCRWLG6M=
|
github.com/go-openapi/errors v0.21.0 h1:FhChC/duCnfoLj1gZ0BgaBmzhJC2SL/sJr8a2vAobSY=
|
||||||
github.com/go-openapi/errors v0.20.4/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk=
|
github.com/go-openapi/errors v0.21.0/go.mod h1:jxNTMUxRCKj65yb/okJGEtahVd7uvWnuWfj53bse4ho=
|
||||||
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/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||||
|
@ -116,8 +116,8 @@ github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6
|
||||||
github.com/go-openapi/spec v0.20.11 h1:J/TzFDLTt4Rcl/l1PmyErvkqlJDncGvPTMnCI39I4gY=
|
github.com/go-openapi/spec v0.20.11 h1:J/TzFDLTt4Rcl/l1PmyErvkqlJDncGvPTMnCI39I4gY=
|
||||||
github.com/go-openapi/spec v0.20.11/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
|
github.com/go-openapi/spec v0.20.11/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
|
||||||
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.9 h1:LnEGOO9qyEC1v22Bzr323M98G13paIUGPU7yeJtG9Xs=
|
github.com/go-openapi/strfmt v0.21.10 h1:JIsly3KXZB/Qf4UzvzJpg4OELH/0ASDQsyk//TTBDDk=
|
||||||
github.com/go-openapi/strfmt v0.21.9/go.mod h1:0k3v301mglEaZRJdDDGSlN6Npq4VMVU69DE0LUyf7uA=
|
github.com/go-openapi/strfmt v0.21.10/go.mod h1:vNDMwbilnl7xKiO/Ve/8H8Bb2JIInBnH+lqiw6QWgis=
|
||||||
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=
|
||||||
|
@ -309,8 +309,8 @@ go.opentelemetry.io/otel/trace v1.17.0 h1:/SWhSRHmDPOImIAetP1QAeMnZYiQXrTy4fMMYO
|
||||||
go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY=
|
go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY=
|
||||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
go.uber.org/goleak v1.2.1 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.2 h1:TV3SdaPGhjVE7If0l1kt+H23xwgEabWUFgX4ijkkeSc=
|
goauthentik.io/api/v3 v3.2023104.3 h1:MzwdB21Q+G+wACEZiX0T1iVV4l7PjopjaVv6muqJE1M=
|
||||||
goauthentik.io/api/v3 v3.2023104.2/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
goauthentik.io/api/v3 v3.2023104.3/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
@ -320,8 +320,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.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
golang.org/x/crypto v0.17.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=
|
||||||
|
|
|
@ -76,7 +76,6 @@ func NewAPIController(akURL url.URL, token string) *APIController {
|
||||||
// Because we don't know the outpost UUID, we simply do a list and pick the first
|
// Because we don't know the outpost UUID, we simply do a list and pick the first
|
||||||
// The service account this token belongs to should only have access to a single outpost
|
// The service account this token belongs to should only have access to a single outpost
|
||||||
outposts, _, err := apiClient.OutpostsApi.OutpostsInstancesList(context.Background()).Execute()
|
outposts, _, err := apiClient.OutpostsApi.OutpostsInstancesList(context.Background()).Execute()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("Failed to fetch outpost configuration, retrying in 3 seconds")
|
log.WithError(err).Error("Failed to fetch outpost configuration, retrying in 3 seconds")
|
||||||
time.Sleep(time.Second * 3)
|
time.Sleep(time.Second * 3)
|
||||||
|
@ -168,7 +167,6 @@ func (a *APIController) OnRefresh() error {
|
||||||
// Because we don't know the outpost UUID, we simply do a list and pick the first
|
// Because we don't know the outpost UUID, we simply do a list and pick the first
|
||||||
// The service account this token belongs to should only have access to a single outpost
|
// The service account this token belongs to should only have access to a single outpost
|
||||||
outposts, _, err := a.Client.OutpostsApi.OutpostsInstancesList(context.Background()).Execute()
|
outposts, _, err := a.Client.OutpostsApi.OutpostsInstancesList(context.Background()).Execute()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("Failed to fetch outpost configuration")
|
log.WithError(err).Error("Failed to fetch outpost configuration")
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -14,8 +14,10 @@ import (
|
||||||
webutils "goauthentik.io/internal/utils/web"
|
webutils "goauthentik.io/internal/utils/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
var initialSetup = false
|
var (
|
||||||
var tlsTransport *http.RoundTripper = nil
|
initialSetup = false
|
||||||
|
tlsTransport *http.RoundTripper = nil
|
||||||
|
)
|
||||||
|
|
||||||
func doGlobalSetup(outpost api.Outpost, globalConfig *api.Config) {
|
func doGlobalSetup(outpost api.Outpost, globalConfig *api.Config) {
|
||||||
l := log.WithField("logger", "authentik.outpost")
|
l := log.WithField("logger", "authentik.outpost")
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
package handler
|
package handler
|
||||||
|
|
||||||
type Handler interface {
|
type Handler interface{}
|
||||||
}
|
|
||||||
|
|
|
@ -78,5 +78,4 @@ func (ls *LDAPServer) fallbackRootDSE(req *search.Request) (ldap.ServerSearchRes
|
||||||
},
|
},
|
||||||
Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldap.LDAPResultSuccess,
|
Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldap.LDAPResultSuccess,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,12 +59,12 @@ func (a *Application) addHeaders(headers http.Header, c *Claims) {
|
||||||
userAttributes := c.Proxy.UserAttributes
|
userAttributes := c.Proxy.UserAttributes
|
||||||
a.setAuthorizationHeader(headers, c)
|
a.setAuthorizationHeader(headers, c)
|
||||||
// Check if user has additional headers set that we should sent
|
// Check if user has additional headers set that we should sent
|
||||||
if additionalHeaders, ok := userAttributes["additionalHeaders"].(map[string]interface{}); ok {
|
if additionalHeaders, ok := userAttributes["additionalHeaders"]; ok {
|
||||||
a.log.WithField("headers", additionalHeaders).Trace("setting additional headers")
|
a.log.WithField("headers", additionalHeaders).Trace("setting additional headers")
|
||||||
if additionalHeaders == nil {
|
if additionalHeaders == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for key, value := range additionalHeaders {
|
for key, value := range additionalHeaders.(map[string]interface{}) {
|
||||||
headers.Set(key, toString(value))
|
headers.Set(key, toString(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package constants
|
package constants
|
||||||
|
|
||||||
const SessionOAuthState = "oauth_state"
|
const (
|
||||||
const SessionClaims = "claims"
|
SessionOAuthState = "oauth_state"
|
||||||
|
SessionClaims = "claims"
|
||||||
|
)
|
||||||
|
|
||||||
const SessionRedirect = "redirect"
|
const SessionRedirect = "redirect"
|
||||||
|
|
||||||
|
|
|
@ -26,26 +26,13 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="pf-c-background-image">
|
<div class="pf-c-background-image">
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-login">
|
<div class="pf-c-login stacked">
|
||||||
<div class="ak-login-container">
|
<div class="ak-login-container">
|
||||||
<header class="pf-c-login__header">
|
<main class="pf-c-login__main">
|
||||||
<div class="pf-c-brand ak-brand">
|
<div class="pf-c-login__main-header pf-c-brand ak-brand">
|
||||||
<img src="/outpost.goauthentik.io/static/dist/assets/icons/icon_left_brand.svg" alt="authentik Logo" />
|
<img src="/outpost.goauthentik.io/static/dist/assets/icons/icon_left_brand.svg" alt="authentik Logo" />
|
||||||
</div>
|
</div>
|
||||||
</header>
|
|
||||||
<main class="pf-c-login__main">
|
|
||||||
<header class="pf-c-login__main-header">
|
<header class="pf-c-login__main-header">
|
||||||
<h1 class="pf-c-title pf-m-3xl">
|
<h1 class="pf-c-title pf-m-3xl">
|
||||||
{{ .Title }}
|
{{ .Title }}
|
||||||
|
|
|
@ -15,7 +15,7 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR
|
||||||
fe := flow.NewFlowExecutor(r.Context(), r.pi.flowSlug, r.pi.s.ac.Client.GetConfig(), log.Fields{
|
fe := flow.NewFlowExecutor(r.Context(), r.pi.flowSlug, r.pi.s.ac.Client.GetConfig(), log.Fields{
|
||||||
"username": username,
|
"username": username,
|
||||||
"client": r.RemoteAddr(),
|
"client": r.RemoteAddr(),
|
||||||
"requestId": r.ID,
|
"requestId": r.ID(),
|
||||||
})
|
})
|
||||||
fe.DelegateClientIP(r.RemoteAddr())
|
fe.DelegateClientIP(r.RemoteAddr())
|
||||||
fe.Params.Add("goauthentik.io/outpost/radius", "true")
|
fe.Params.Add("goauthentik.io/outpost/radius", "true")
|
||||||
|
@ -27,7 +27,6 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR
|
||||||
}
|
}
|
||||||
|
|
||||||
passed, err := fe.Execute()
|
passed, err := fe.Execute()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Log().WithField("username", username).WithError(err).Warning("failed to execute flow")
|
r.Log().WithField("username", username).WithError(err).Warning("failed to execute flow")
|
||||||
metrics.RequestsRejected.With(prometheus.Labels{
|
metrics.RequestsRejected.With(prometheus.Labels{
|
||||||
|
|
|
@ -14,12 +14,10 @@ import (
|
||||||
"goauthentik.io/internal/utils/sentry"
|
"goauthentik.io/internal/utils/sentry"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var Requests = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||||
Requests = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
|
||||||
Name: "authentik_main_request_duration_seconds",
|
Name: "authentik_main_request_duration_seconds",
|
||||||
Help: "API request latencies in seconds",
|
Help: "API request latencies in seconds",
|
||||||
}, []string{"dest"})
|
}, []string{"dest"})
|
||||||
)
|
|
||||||
|
|
||||||
func (ws *WebServer) runMetricsServer() {
|
func (ws *WebServer) runMetricsServer() {
|
||||||
m := mux.NewRouter()
|
m := mux.NewRouter()
|
||||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-12-18 09:56+0000\n"
|
"POT-Creation-Date: 2023-12-20 08:41+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -100,170 +100,170 @@ msgstr ""
|
||||||
msgid "Create a SAML Provider by importing its Metadata."
|
msgid "Create a SAML Provider by importing its Metadata."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/api/users.py:155
|
#: authentik/core/api/users.py:149
|
||||||
msgid "No leading or trailing slashes allowed."
|
msgid "No leading or trailing slashes allowed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/api/users.py:158
|
#: authentik/core/api/users.py:152
|
||||||
msgid "No empty segments in user path allowed."
|
msgid "No empty segments in user path allowed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:86
|
#: authentik/core/models.py:85
|
||||||
msgid "name"
|
msgid "name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:88
|
#: authentik/core/models.py:87
|
||||||
msgid "Users added to this group will be superusers."
|
msgid "Users added to this group will be superusers."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:162
|
#: authentik/core/models.py:161
|
||||||
msgid "Group"
|
msgid "Group"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:163
|
#: authentik/core/models.py:162
|
||||||
msgid "Groups"
|
msgid "Groups"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:178
|
#: authentik/core/models.py:177
|
||||||
msgid "User's display name."
|
msgid "User's display name."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:274 authentik/providers/oauth2/models.py:295
|
#: authentik/core/models.py:273 authentik/providers/oauth2/models.py:295
|
||||||
msgid "User"
|
msgid "User"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:275
|
#: authentik/core/models.py:274
|
||||||
msgid "Users"
|
msgid "Users"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:277
|
#: authentik/core/models.py:276
|
||||||
#: authentik/stages/email/templates/email/password_reset.html:28
|
#: authentik/stages/email/templates/email/password_reset.html:28
|
||||||
msgid "Reset Password"
|
msgid "Reset Password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:278
|
#: authentik/core/models.py:277
|
||||||
msgid "Can impersonate other users"
|
msgid "Can impersonate other users"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:279 authentik/rbac/models.py:54
|
#: authentik/core/models.py:278 authentik/rbac/models.py:54
|
||||||
msgid "Can assign permissions to users"
|
msgid "Can assign permissions to users"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:280 authentik/rbac/models.py:55
|
#: authentik/core/models.py:279 authentik/rbac/models.py:55
|
||||||
msgid "Can unassign permissions from users"
|
msgid "Can unassign permissions from users"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:294
|
#: authentik/core/models.py:293
|
||||||
msgid ""
|
msgid ""
|
||||||
"Flow used for authentication when the associated application is accessed by "
|
"Flow used for authentication when the associated application is accessed by "
|
||||||
"an un-authenticated user."
|
"an un-authenticated user."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:304
|
#: authentik/core/models.py:303
|
||||||
msgid "Flow used when authorizing this provider."
|
msgid "Flow used when authorizing this provider."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:316
|
#: authentik/core/models.py:315
|
||||||
msgid ""
|
msgid ""
|
||||||
"Accessed from applications; optional backchannel providers for protocols "
|
"Accessed from applications; optional backchannel providers for protocols "
|
||||||
"like LDAP and SCIM."
|
"like LDAP and SCIM."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:371
|
#: authentik/core/models.py:370
|
||||||
msgid "Application's display Name."
|
msgid "Application's display Name."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:372
|
#: authentik/core/models.py:371
|
||||||
msgid "Internal application name, used in URLs."
|
msgid "Internal application name, used in URLs."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:384
|
#: authentik/core/models.py:383
|
||||||
msgid "Open launch URL in a new browser tab or window."
|
msgid "Open launch URL in a new browser tab or window."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:448
|
#: authentik/core/models.py:447
|
||||||
msgid "Application"
|
msgid "Application"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:449
|
#: authentik/core/models.py:448
|
||||||
msgid "Applications"
|
msgid "Applications"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:455
|
#: authentik/core/models.py:454
|
||||||
msgid "Use the source-specific identifier"
|
msgid "Use the source-specific identifier"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:457
|
#: authentik/core/models.py:456
|
||||||
msgid ""
|
msgid ""
|
||||||
"Link to a user with identical email address. Can have security implications "
|
"Link to a user with identical email address. Can have security implications "
|
||||||
"when a source doesn't validate email addresses."
|
"when a source doesn't validate email addresses."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:461
|
#: authentik/core/models.py:460
|
||||||
msgid ""
|
msgid ""
|
||||||
"Use the user's email address, but deny enrollment when the email address "
|
"Use the user's email address, but deny enrollment when the email address "
|
||||||
"already exists."
|
"already exists."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:464
|
#: authentik/core/models.py:463
|
||||||
msgid ""
|
msgid ""
|
||||||
"Link to a user with identical username. Can have security implications when "
|
"Link to a user with identical username. Can have security implications when "
|
||||||
"a username is used with another source."
|
"a username is used with another source."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:468
|
#: authentik/core/models.py:467
|
||||||
msgid ""
|
msgid ""
|
||||||
"Use the user's username, but deny enrollment when the username already "
|
"Use the user's username, but deny enrollment when the username already "
|
||||||
"exists."
|
"exists."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:475
|
#: authentik/core/models.py:474
|
||||||
msgid "Source's display Name."
|
msgid "Source's display Name."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:476
|
#: authentik/core/models.py:475
|
||||||
msgid "Internal source name, used in URLs."
|
msgid "Internal source name, used in URLs."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:495
|
#: authentik/core/models.py:494
|
||||||
msgid "Flow to use when authenticating existing users."
|
msgid "Flow to use when authenticating existing users."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:504
|
#: authentik/core/models.py:503
|
||||||
msgid "Flow to use when enrolling new users."
|
msgid "Flow to use when enrolling new users."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:512
|
#: authentik/core/models.py:511
|
||||||
msgid ""
|
msgid ""
|
||||||
"How the source determines if an existing user should be authenticated or a "
|
"How the source determines if an existing user should be authenticated or a "
|
||||||
"new user enrolled."
|
"new user enrolled."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:684
|
#: authentik/core/models.py:683
|
||||||
msgid "Token"
|
msgid "Token"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:685
|
#: authentik/core/models.py:684
|
||||||
msgid "Tokens"
|
msgid "Tokens"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:690
|
#: authentik/core/models.py:689
|
||||||
msgid "View token's key"
|
msgid "View token's key"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:726
|
#: authentik/core/models.py:725
|
||||||
msgid "Property Mapping"
|
msgid "Property Mapping"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:727
|
#: authentik/core/models.py:726
|
||||||
msgid "Property Mappings"
|
msgid "Property Mappings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:762
|
#: authentik/core/models.py:763
|
||||||
msgid "Authenticated Session"
|
msgid "Authenticated Session"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/models.py:763
|
#: authentik/core/models.py:764
|
||||||
msgid "Authenticated Sessions"
|
msgid "Authenticated Sessions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -338,7 +338,7 @@ msgstr ""
|
||||||
msgid "Go home"
|
msgid "Go home"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/core/templates/login/base_full.html:90
|
#: authentik/core/templates/login/base_full.html:75
|
||||||
msgid "Powered by authentik"
|
msgid "Powered by authentik"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -388,105 +388,105 @@ msgstr ""
|
||||||
msgid "License Usage Records"
|
msgid "License Usage Records"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:294
|
#: authentik/events/models.py:295
|
||||||
msgid "Event"
|
msgid "Event"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:295
|
#: authentik/events/models.py:296
|
||||||
msgid "Events"
|
msgid "Events"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:301
|
#: authentik/events/models.py:302
|
||||||
msgid "authentik inbuilt notifications"
|
msgid "authentik inbuilt notifications"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:302
|
#: authentik/events/models.py:303
|
||||||
msgid "Generic Webhook"
|
msgid "Generic Webhook"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:303
|
#: authentik/events/models.py:304
|
||||||
msgid "Slack Webhook (Slack/Discord)"
|
msgid "Slack Webhook (Slack/Discord)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:304
|
#: authentik/events/models.py:305
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:322
|
#: authentik/events/models.py:323
|
||||||
msgid ""
|
msgid ""
|
||||||
"Only send notification once, for example when sending a webhook into a chat "
|
"Only send notification once, for example when sending a webhook into a chat "
|
||||||
"channel."
|
"channel."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:387
|
#: authentik/events/models.py:388
|
||||||
msgid "Severity"
|
msgid "Severity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:392
|
#: authentik/events/models.py:393
|
||||||
msgid "Dispatched for user"
|
msgid "Dispatched for user"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:401
|
#: authentik/events/models.py:402
|
||||||
msgid "Event user"
|
msgid "Event user"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:495
|
#: authentik/events/models.py:496
|
||||||
msgid "Notification Transport"
|
msgid "Notification Transport"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:496
|
#: authentik/events/models.py:497
|
||||||
msgid "Notification Transports"
|
msgid "Notification Transports"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:502
|
#: authentik/events/models.py:503
|
||||||
msgid "Notice"
|
msgid "Notice"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:503
|
#: authentik/events/models.py:504
|
||||||
msgid "Warning"
|
msgid "Warning"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:504
|
#: authentik/events/models.py:505
|
||||||
msgid "Alert"
|
msgid "Alert"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:529
|
#: authentik/events/models.py:530
|
||||||
msgid "Notification"
|
msgid "Notification"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:530
|
#: authentik/events/models.py:531
|
||||||
msgid "Notifications"
|
msgid "Notifications"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:540
|
#: authentik/events/models.py:541
|
||||||
msgid ""
|
msgid ""
|
||||||
"Select which transports should be used to notify the user. If none are "
|
"Select which transports should be used to notify the user. If none are "
|
||||||
"selected, the notification will only be shown in the authentik UI."
|
"selected, the notification will only be shown in the authentik UI."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:548
|
#: authentik/events/models.py:549
|
||||||
msgid "Controls which severity level the created notifications will have."
|
msgid "Controls which severity level the created notifications will have."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:553
|
#: authentik/events/models.py:554
|
||||||
msgid ""
|
msgid ""
|
||||||
"Define which group of users this notification should be sent and shown to. "
|
"Define which group of users this notification should be sent and shown to. "
|
||||||
"If left empty, Notification won't ben sent."
|
"If left empty, Notification won't ben sent."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:571
|
#: authentik/events/models.py:572
|
||||||
msgid "Notification Rule"
|
msgid "Notification Rule"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:572
|
#: authentik/events/models.py:573
|
||||||
msgid "Notification Rules"
|
msgid "Notification Rules"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:592
|
#: authentik/events/models.py:593
|
||||||
msgid "Webhook Mapping"
|
msgid "Webhook Mapping"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/events/models.py:593
|
#: authentik/events/models.py:594
|
||||||
msgid "Webhook Mappings"
|
msgid "Webhook Mappings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -547,7 +547,7 @@ msgstr ""
|
||||||
msgid "Pre-flow policies"
|
msgid "Pre-flow policies"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/api/flows_diagram.py:214 authentik/flows/models.py:193
|
#: authentik/flows/api/flows_diagram.py:214 authentik/flows/models.py:194
|
||||||
msgid "Flow"
|
msgid "Flow"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -555,72 +555,72 @@ msgstr ""
|
||||||
msgid "Flow does not apply to current user."
|
msgid "Flow does not apply to current user."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:114
|
#: authentik/flows/models.py:115
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Dynamic In-memory stage: %(doc)s"
|
msgid "Dynamic In-memory stage: %(doc)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:129
|
#: authentik/flows/models.py:130
|
||||||
msgid "Visible in the URL."
|
msgid "Visible in the URL."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:131
|
#: authentik/flows/models.py:132
|
||||||
msgid "Shown as the Title in Flow pages."
|
msgid "Shown as the Title in Flow pages."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:138
|
#: authentik/flows/models.py:139
|
||||||
msgid ""
|
msgid ""
|
||||||
"Decides what this Flow is used for. For example, the Authentication flow is "
|
"Decides what this Flow is used for. For example, the Authentication flow is "
|
||||||
"redirect to when an un-authenticated user visits authentik."
|
"redirect to when an un-authenticated user visits authentik."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:147
|
#: authentik/flows/models.py:148
|
||||||
msgid "Background shown during execution"
|
msgid "Background shown during execution"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:154
|
#: authentik/flows/models.py:155
|
||||||
msgid ""
|
msgid ""
|
||||||
"Enable compatibility mode, increases compatibility with password managers on "
|
"Enable compatibility mode, increases compatibility with password managers on "
|
||||||
"mobile devices."
|
"mobile devices."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:162
|
#: authentik/flows/models.py:163
|
||||||
msgid "Configure what should happen when a flow denies access to a user."
|
msgid "Configure what should happen when a flow denies access to a user."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:168
|
#: authentik/flows/models.py:169
|
||||||
msgid "Required level of authentication and authorization to access a flow."
|
msgid "Required level of authentication and authorization to access a flow."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:194
|
#: authentik/flows/models.py:195
|
||||||
msgid "Flows"
|
msgid "Flows"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:197
|
#: authentik/flows/models.py:198
|
||||||
msgid "Can export a Flow"
|
msgid "Can export a Flow"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:198
|
#: authentik/flows/models.py:199
|
||||||
msgid "Can inspect a Flow's execution"
|
msgid "Can inspect a Flow's execution"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:199
|
#: authentik/flows/models.py:200
|
||||||
msgid "View Flow's cache metrics"
|
msgid "View Flow's cache metrics"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:200
|
#: authentik/flows/models.py:201
|
||||||
msgid "Clear Flow's cache metrics"
|
msgid "Clear Flow's cache metrics"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:216
|
#: authentik/flows/models.py:217
|
||||||
msgid "Evaluate policies during the Flow planning process."
|
msgid "Evaluate policies during the Flow planning process."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:220
|
#: authentik/flows/models.py:221
|
||||||
msgid "Evaluate policies when the Stage is present to the user."
|
msgid "Evaluate policies when the Stage is present to the user."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:227
|
#: authentik/flows/models.py:228
|
||||||
msgid ""
|
msgid ""
|
||||||
"Configure how the flow executor should handle an invalid response to a "
|
"Configure how the flow executor should handle an invalid response to a "
|
||||||
"challenge. RETRY returns the error message and a similar challenge to the "
|
"challenge. RETRY returns the error message and a similar challenge to the "
|
||||||
|
@ -628,25 +628,25 @@ msgid ""
|
||||||
"RESTART_WITH_CONTEXT restarts the flow while keeping the current context."
|
"RESTART_WITH_CONTEXT restarts the flow while keeping the current context."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:250
|
#: authentik/flows/models.py:251
|
||||||
msgid "Flow Stage Binding"
|
msgid "Flow Stage Binding"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:251
|
#: authentik/flows/models.py:252
|
||||||
msgid "Flow Stage Bindings"
|
msgid "Flow Stage Bindings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:266
|
#: authentik/flows/models.py:267
|
||||||
msgid ""
|
msgid ""
|
||||||
"Flow used by an authenticated user to configure this Stage. If empty, user "
|
"Flow used by an authenticated user to configure this Stage. If empty, user "
|
||||||
"will not be able to configure this stage."
|
"will not be able to configure this stage."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:306
|
#: authentik/flows/models.py:307
|
||||||
msgid "Flow Token"
|
msgid "Flow Token"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/flows/models.py:307
|
#: authentik/flows/models.py:308
|
||||||
msgid "Flow Tokens"
|
msgid "Flow Tokens"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1526,27 +1526,27 @@ msgstr ""
|
||||||
msgid "Starting full SCIM sync"
|
msgid "Starting full SCIM sync"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/providers/scim/tasks.py:59
|
#: authentik/providers/scim/tasks.py:62
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Syncing page %(page)d of users"
|
msgid "Syncing page %(page)d of users"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/providers/scim/tasks.py:63
|
#: authentik/providers/scim/tasks.py:66
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Syncing page %(page)d of groups"
|
msgid "Syncing page %(page)d of groups"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/providers/scim/tasks.py:92
|
#: authentik/providers/scim/tasks.py:98
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Failed to sync user %(user_name)s due to remote error: %(error)s"
|
msgid "Failed to sync user %(user_name)s due to remote error: %(error)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/providers/scim/tasks.py:103 authentik/providers/scim/tasks.py:144
|
#: authentik/providers/scim/tasks.py:109 authentik/providers/scim/tasks.py:150
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Stopping sync due to error: %(error)s"
|
msgid "Stopping sync due to error: %(error)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/providers/scim/tasks.py:133
|
#: authentik/providers/scim/tasks.py:139
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Failed to sync group %(group_name)s due to remote error: %(error)s"
|
msgid "Failed to sync group %(group_name)s due to remote error: %(error)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
663
schema.yml
663
schema.yml
|
@ -28487,46 +28487,6 @@ components:
|
||||||
* `authentik.blueprints` - authentik Blueprints
|
* `authentik.blueprints` - authentik Blueprints
|
||||||
* `authentik.core` - authentik Core
|
* `authentik.core` - authentik Core
|
||||||
* `authentik.enterprise` - authentik Enterprise
|
* `authentik.enterprise` - authentik Enterprise
|
||||||
AppleChallengeResponseRequest:
|
|
||||||
type: object
|
|
||||||
description: Pseudo class for plex response
|
|
||||||
properties:
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
default: ak-source-oauth-apple
|
|
||||||
AppleLoginChallenge:
|
|
||||||
type: object
|
|
||||||
description: Special challenge for apple-native authentication flow, which happens
|
|
||||||
on the client.
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
$ref: '#/components/schemas/ChallengeChoices'
|
|
||||||
flow_info:
|
|
||||||
$ref: '#/components/schemas/ContextualFlowInfo'
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
default: ak-source-oauth-apple
|
|
||||||
response_errors:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/ErrorDetail'
|
|
||||||
client_id:
|
|
||||||
type: string
|
|
||||||
scope:
|
|
||||||
type: string
|
|
||||||
redirect_uri:
|
|
||||||
type: string
|
|
||||||
state:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- client_id
|
|
||||||
- redirect_uri
|
|
||||||
- scope
|
|
||||||
- state
|
|
||||||
- type
|
|
||||||
Application:
|
Application:
|
||||||
type: object
|
type: object
|
||||||
description: Application Serializer
|
description: Application Serializer
|
||||||
|
@ -28850,12 +28810,14 @@ components:
|
||||||
- require_authenticated
|
- require_authenticated
|
||||||
- require_unauthenticated
|
- require_unauthenticated
|
||||||
- require_superuser
|
- require_superuser
|
||||||
|
- require_outpost
|
||||||
type: string
|
type: string
|
||||||
description: |-
|
description: |-
|
||||||
* `none` - None
|
* `none` - None
|
||||||
* `require_authenticated` - Require Authenticated
|
* `require_authenticated` - Require Authenticated
|
||||||
* `require_unauthenticated` - Require Unauthenticated
|
* `require_unauthenticated` - Require Unauthenticated
|
||||||
* `require_superuser` - Require Superuser
|
* `require_superuser` - Require Superuser
|
||||||
|
* `require_outpost` - Require Outpost
|
||||||
AuthenticatorAttachmentEnum:
|
AuthenticatorAttachmentEnum:
|
||||||
enum:
|
enum:
|
||||||
- platform
|
- platform
|
||||||
|
@ -29026,47 +28988,6 @@ components:
|
||||||
- client_id
|
- client_id
|
||||||
- client_secret
|
- client_secret
|
||||||
- name
|
- name
|
||||||
AuthenticatorSMSChallenge:
|
|
||||||
type: object
|
|
||||||
description: SMS Setup challenge
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
$ref: '#/components/schemas/ChallengeChoices'
|
|
||||||
flow_info:
|
|
||||||
$ref: '#/components/schemas/ContextualFlowInfo'
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
default: ak-stage-authenticator-sms
|
|
||||||
response_errors:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/ErrorDetail'
|
|
||||||
pending_user:
|
|
||||||
type: string
|
|
||||||
pending_user_avatar:
|
|
||||||
type: string
|
|
||||||
phone_number_required:
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
required:
|
|
||||||
- pending_user
|
|
||||||
- pending_user_avatar
|
|
||||||
- type
|
|
||||||
AuthenticatorSMSChallengeResponseRequest:
|
|
||||||
type: object
|
|
||||||
description: SMS Challenge response, device is set by get_response_instance
|
|
||||||
properties:
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
default: ak-stage-authenticator-sms
|
|
||||||
code:
|
|
||||||
type: integer
|
|
||||||
phone_number:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
AuthenticatorSMSStage:
|
AuthenticatorSMSStage:
|
||||||
type: object
|
type: object
|
||||||
description: AuthenticatorSMSStage Serializer
|
description: AuthenticatorSMSStage Serializer
|
||||||
|
@ -29192,44 +29113,6 @@ components:
|
||||||
- from_number
|
- from_number
|
||||||
- name
|
- name
|
||||||
- provider
|
- provider
|
||||||
AuthenticatorStaticChallenge:
|
|
||||||
type: object
|
|
||||||
description: Static authenticator challenge
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
$ref: '#/components/schemas/ChallengeChoices'
|
|
||||||
flow_info:
|
|
||||||
$ref: '#/components/schemas/ContextualFlowInfo'
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
default: ak-stage-authenticator-static
|
|
||||||
response_errors:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/ErrorDetail'
|
|
||||||
pending_user:
|
|
||||||
type: string
|
|
||||||
pending_user_avatar:
|
|
||||||
type: string
|
|
||||||
codes:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- codes
|
|
||||||
- pending_user
|
|
||||||
- pending_user_avatar
|
|
||||||
- type
|
|
||||||
AuthenticatorStaticChallengeResponseRequest:
|
|
||||||
type: object
|
|
||||||
description: Pseudo class for static response
|
|
||||||
properties:
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
default: ak-stage-authenticator-static
|
|
||||||
AuthenticatorStaticStage:
|
AuthenticatorStaticStage:
|
||||||
type: object
|
type: object
|
||||||
description: AuthenticatorStaticStage Serializer
|
description: AuthenticatorStaticStage Serializer
|
||||||
|
@ -29316,46 +29199,6 @@ components:
|
||||||
minimum: 0
|
minimum: 0
|
||||||
required:
|
required:
|
||||||
- name
|
- name
|
||||||
AuthenticatorTOTPChallenge:
|
|
||||||
type: object
|
|
||||||
description: TOTP Setup challenge
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
$ref: '#/components/schemas/ChallengeChoices'
|
|
||||||
flow_info:
|
|
||||||
$ref: '#/components/schemas/ContextualFlowInfo'
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
default: ak-stage-authenticator-totp
|
|
||||||
response_errors:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/ErrorDetail'
|
|
||||||
pending_user:
|
|
||||||
type: string
|
|
||||||
pending_user_avatar:
|
|
||||||
type: string
|
|
||||||
config_url:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- config_url
|
|
||||||
- pending_user
|
|
||||||
- pending_user_avatar
|
|
||||||
- type
|
|
||||||
AuthenticatorTOTPChallengeResponseRequest:
|
|
||||||
type: object
|
|
||||||
description: TOTP Challenge response, device is set by get_response_instance
|
|
||||||
properties:
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
default: ak-stage-authenticator-totp
|
|
||||||
code:
|
|
||||||
type: integer
|
|
||||||
required:
|
|
||||||
- code
|
|
||||||
AuthenticatorTOTPStage:
|
AuthenticatorTOTPStage:
|
||||||
type: object
|
type: object
|
||||||
description: AuthenticatorTOTPStage Serializer
|
description: AuthenticatorTOTPStage Serializer
|
||||||
|
@ -29540,104 +29383,6 @@ components:
|
||||||
* `discouraged` - Discouraged
|
* `discouraged` - Discouraged
|
||||||
required:
|
required:
|
||||||
- name
|
- name
|
||||||
AuthenticatorValidationChallenge:
|
|
||||||
type: object
|
|
||||||
description: Authenticator challenge
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
$ref: '#/components/schemas/ChallengeChoices'
|
|
||||||
flow_info:
|
|
||||||
$ref: '#/components/schemas/ContextualFlowInfo'
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
default: ak-stage-authenticator-validate
|
|
||||||
response_errors:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/ErrorDetail'
|
|
||||||
pending_user:
|
|
||||||
type: string
|
|
||||||
pending_user_avatar:
|
|
||||||
type: string
|
|
||||||
device_challenges:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/DeviceChallenge'
|
|
||||||
configuration_stages:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/SelectableStage'
|
|
||||||
required:
|
|
||||||
- configuration_stages
|
|
||||||
- device_challenges
|
|
||||||
- pending_user
|
|
||||||
- pending_user_avatar
|
|
||||||
- type
|
|
||||||
AuthenticatorValidationChallengeResponseRequest:
|
|
||||||
type: object
|
|
||||||
description: Challenge used for Code-based and WebAuthn authenticators
|
|
||||||
properties:
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
default: ak-stage-authenticator-validate
|
|
||||||
selected_challenge:
|
|
||||||
$ref: '#/components/schemas/DeviceChallengeRequest'
|
|
||||||
selected_stage:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
code:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
webauthn:
|
|
||||||
type: object
|
|
||||||
additionalProperties: {}
|
|
||||||
duo:
|
|
||||||
type: integer
|
|
||||||
AuthenticatorWebAuthnChallenge:
|
|
||||||
type: object
|
|
||||||
description: WebAuthn Challenge
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
$ref: '#/components/schemas/ChallengeChoices'
|
|
||||||
flow_info:
|
|
||||||
$ref: '#/components/schemas/ContextualFlowInfo'
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
default: ak-stage-authenticator-webauthn
|
|
||||||
response_errors:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/ErrorDetail'
|
|
||||||
pending_user:
|
|
||||||
type: string
|
|
||||||
pending_user_avatar:
|
|
||||||
type: string
|
|
||||||
registration:
|
|
||||||
type: object
|
|
||||||
additionalProperties: {}
|
|
||||||
required:
|
|
||||||
- pending_user
|
|
||||||
- pending_user_avatar
|
|
||||||
- registration
|
|
||||||
- type
|
|
||||||
AuthenticatorWebAuthnChallengeResponseRequest:
|
|
||||||
type: object
|
|
||||||
description: WebAuthn Challenge response
|
|
||||||
properties:
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
default: ak-stage-authenticator-webauthn
|
|
||||||
response:
|
|
||||||
type: object
|
|
||||||
additionalProperties: {}
|
|
||||||
required:
|
|
||||||
- response
|
|
||||||
AutoSubmitChallengeResponseRequest:
|
AutoSubmitChallengeResponseRequest:
|
||||||
type: object
|
type: object
|
||||||
description: Pseudo class for autosubmit response
|
description: Pseudo class for autosubmit response
|
||||||
|
@ -29928,50 +29673,6 @@ components:
|
||||||
* `can_impersonate` - Can Impersonate
|
* `can_impersonate` - Can Impersonate
|
||||||
* `can_debug` - Can Debug
|
* `can_debug` - Can Debug
|
||||||
* `is_enterprise` - Is Enterprise
|
* `is_enterprise` - Is Enterprise
|
||||||
CaptchaChallenge:
|
|
||||||
type: object
|
|
||||||
description: Site public key
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
$ref: '#/components/schemas/ChallengeChoices'
|
|
||||||
flow_info:
|
|
||||||
$ref: '#/components/schemas/ContextualFlowInfo'
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
default: ak-stage-captcha
|
|
||||||
response_errors:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/ErrorDetail'
|
|
||||||
pending_user:
|
|
||||||
type: string
|
|
||||||
pending_user_avatar:
|
|
||||||
type: string
|
|
||||||
site_key:
|
|
||||||
type: string
|
|
||||||
js_url:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- js_url
|
|
||||||
- pending_user
|
|
||||||
- pending_user_avatar
|
|
||||||
- site_key
|
|
||||||
- type
|
|
||||||
CaptchaChallengeResponseRequest:
|
|
||||||
type: object
|
|
||||||
description: Validate captcha token
|
|
||||||
properties:
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
default: ak-stage-captcha
|
|
||||||
token:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
required:
|
|
||||||
- token
|
|
||||||
CaptchaStage:
|
CaptchaStage:
|
||||||
type: object
|
type: object
|
||||||
description: CaptchaStage Serializer
|
description: CaptchaStage Serializer
|
||||||
|
@ -30174,20 +29875,10 @@ components:
|
||||||
ChallengeTypes:
|
ChallengeTypes:
|
||||||
oneOf:
|
oneOf:
|
||||||
- $ref: '#/components/schemas/AccessDeniedChallenge'
|
- $ref: '#/components/schemas/AccessDeniedChallenge'
|
||||||
- $ref: '#/components/schemas/AppleLoginChallenge'
|
|
||||||
- $ref: '#/components/schemas/AuthenticatorDuoChallenge'
|
- $ref: '#/components/schemas/AuthenticatorDuoChallenge'
|
||||||
- $ref: '#/components/schemas/AuthenticatorSMSChallenge'
|
|
||||||
- $ref: '#/components/schemas/AuthenticatorStaticChallenge'
|
|
||||||
- $ref: '#/components/schemas/AuthenticatorTOTPChallenge'
|
|
||||||
- $ref: '#/components/schemas/AuthenticatorValidationChallenge'
|
|
||||||
- $ref: '#/components/schemas/AuthenticatorWebAuthnChallenge'
|
|
||||||
- $ref: '#/components/schemas/AutosubmitChallenge'
|
- $ref: '#/components/schemas/AutosubmitChallenge'
|
||||||
- $ref: '#/components/schemas/CaptchaChallenge'
|
|
||||||
- $ref: '#/components/schemas/ConsentChallenge'
|
- $ref: '#/components/schemas/ConsentChallenge'
|
||||||
- $ref: '#/components/schemas/DummyChallenge'
|
|
||||||
- $ref: '#/components/schemas/EmailChallenge'
|
|
||||||
- $ref: '#/components/schemas/FlowErrorChallenge'
|
- $ref: '#/components/schemas/FlowErrorChallenge'
|
||||||
- $ref: '#/components/schemas/IdentificationChallenge'
|
|
||||||
- $ref: '#/components/schemas/OAuthDeviceCodeChallenge'
|
- $ref: '#/components/schemas/OAuthDeviceCodeChallenge'
|
||||||
- $ref: '#/components/schemas/OAuthDeviceCodeFinishChallenge'
|
- $ref: '#/components/schemas/OAuthDeviceCodeFinishChallenge'
|
||||||
- $ref: '#/components/schemas/PasswordChallenge'
|
- $ref: '#/components/schemas/PasswordChallenge'
|
||||||
|
@ -30195,25 +29886,14 @@ components:
|
||||||
- $ref: '#/components/schemas/PromptChallenge'
|
- $ref: '#/components/schemas/PromptChallenge'
|
||||||
- $ref: '#/components/schemas/RedirectChallenge'
|
- $ref: '#/components/schemas/RedirectChallenge'
|
||||||
- $ref: '#/components/schemas/ShellChallenge'
|
- $ref: '#/components/schemas/ShellChallenge'
|
||||||
- $ref: '#/components/schemas/UserLoginChallenge'
|
|
||||||
discriminator:
|
discriminator:
|
||||||
propertyName: component
|
propertyName: component
|
||||||
mapping:
|
mapping:
|
||||||
ak-stage-access-denied: '#/components/schemas/AccessDeniedChallenge'
|
ak-stage-access-denied: '#/components/schemas/AccessDeniedChallenge'
|
||||||
ak-source-oauth-apple: '#/components/schemas/AppleLoginChallenge'
|
|
||||||
ak-stage-authenticator-duo: '#/components/schemas/AuthenticatorDuoChallenge'
|
ak-stage-authenticator-duo: '#/components/schemas/AuthenticatorDuoChallenge'
|
||||||
ak-stage-authenticator-sms: '#/components/schemas/AuthenticatorSMSChallenge'
|
|
||||||
ak-stage-authenticator-static: '#/components/schemas/AuthenticatorStaticChallenge'
|
|
||||||
ak-stage-authenticator-totp: '#/components/schemas/AuthenticatorTOTPChallenge'
|
|
||||||
ak-stage-authenticator-validate: '#/components/schemas/AuthenticatorValidationChallenge'
|
|
||||||
ak-stage-authenticator-webauthn: '#/components/schemas/AuthenticatorWebAuthnChallenge'
|
|
||||||
ak-stage-autosubmit: '#/components/schemas/AutosubmitChallenge'
|
ak-stage-autosubmit: '#/components/schemas/AutosubmitChallenge'
|
||||||
ak-stage-captcha: '#/components/schemas/CaptchaChallenge'
|
|
||||||
ak-stage-consent: '#/components/schemas/ConsentChallenge'
|
ak-stage-consent: '#/components/schemas/ConsentChallenge'
|
||||||
ak-stage-dummy: '#/components/schemas/DummyChallenge'
|
|
||||||
ak-stage-email: '#/components/schemas/EmailChallenge'
|
|
||||||
ak-stage-flow-error: '#/components/schemas/FlowErrorChallenge'
|
ak-stage-flow-error: '#/components/schemas/FlowErrorChallenge'
|
||||||
ak-stage-identification: '#/components/schemas/IdentificationChallenge'
|
|
||||||
ak-provider-oauth2-device-code: '#/components/schemas/OAuthDeviceCodeChallenge'
|
ak-provider-oauth2-device-code: '#/components/schemas/OAuthDeviceCodeChallenge'
|
||||||
ak-provider-oauth2-device-code-finish: '#/components/schemas/OAuthDeviceCodeFinishChallenge'
|
ak-provider-oauth2-device-code-finish: '#/components/schemas/OAuthDeviceCodeFinishChallenge'
|
||||||
ak-stage-password: '#/components/schemas/PasswordChallenge'
|
ak-stage-password: '#/components/schemas/PasswordChallenge'
|
||||||
|
@ -30221,7 +29901,6 @@ components:
|
||||||
ak-stage-prompt: '#/components/schemas/PromptChallenge'
|
ak-stage-prompt: '#/components/schemas/PromptChallenge'
|
||||||
xak-flow-redirect: '#/components/schemas/RedirectChallenge'
|
xak-flow-redirect: '#/components/schemas/RedirectChallenge'
|
||||||
xak-flow-shell: '#/components/schemas/ShellChallenge'
|
xak-flow-shell: '#/components/schemas/ShellChallenge'
|
||||||
ak-stage-user-login: '#/components/schemas/UserLoginChallenge'
|
|
||||||
ClientTypeEnum:
|
ClientTypeEnum:
|
||||||
enum:
|
enum:
|
||||||
- confidential
|
- confidential
|
||||||
|
@ -30404,7 +30083,7 @@ components:
|
||||||
cancel_url:
|
cancel_url:
|
||||||
type: string
|
type: string
|
||||||
layout:
|
layout:
|
||||||
$ref: '#/components/schemas/LayoutEnum'
|
$ref: '#/components/schemas/FlowLayoutEnum'
|
||||||
required:
|
required:
|
||||||
- cancel_url
|
- cancel_url
|
||||||
- layout
|
- layout
|
||||||
|
@ -30565,38 +30244,6 @@ components:
|
||||||
- type
|
- type
|
||||||
- verbose_name
|
- verbose_name
|
||||||
- verbose_name_plural
|
- verbose_name_plural
|
||||||
DeviceChallenge:
|
|
||||||
type: object
|
|
||||||
description: Single device challenge
|
|
||||||
properties:
|
|
||||||
device_class:
|
|
||||||
type: string
|
|
||||||
device_uid:
|
|
||||||
type: string
|
|
||||||
challenge:
|
|
||||||
type: object
|
|
||||||
additionalProperties: {}
|
|
||||||
required:
|
|
||||||
- challenge
|
|
||||||
- device_class
|
|
||||||
- device_uid
|
|
||||||
DeviceChallengeRequest:
|
|
||||||
type: object
|
|
||||||
description: Single device challenge
|
|
||||||
properties:
|
|
||||||
device_class:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
device_uid:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
challenge:
|
|
||||||
type: object
|
|
||||||
additionalProperties: {}
|
|
||||||
required:
|
|
||||||
- challenge
|
|
||||||
- device_class
|
|
||||||
- device_uid
|
|
||||||
DeviceClassesEnum:
|
DeviceClassesEnum:
|
||||||
enum:
|
enum:
|
||||||
- static
|
- static
|
||||||
|
@ -30753,33 +30400,6 @@ components:
|
||||||
required:
|
required:
|
||||||
- domain
|
- domain
|
||||||
- tenant
|
- tenant
|
||||||
DummyChallenge:
|
|
||||||
type: object
|
|
||||||
description: Dummy challenge
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
$ref: '#/components/schemas/ChallengeChoices'
|
|
||||||
flow_info:
|
|
||||||
$ref: '#/components/schemas/ContextualFlowInfo'
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
default: ak-stage-dummy
|
|
||||||
response_errors:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/ErrorDetail'
|
|
||||||
required:
|
|
||||||
- type
|
|
||||||
DummyChallengeResponseRequest:
|
|
||||||
type: object
|
|
||||||
description: Dummy challenge response
|
|
||||||
properties:
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
default: ak-stage-dummy
|
|
||||||
DummyPolicy:
|
DummyPolicy:
|
||||||
type: object
|
type: object
|
||||||
description: Dummy Policy Serializer
|
description: Dummy Policy Serializer
|
||||||
|
@ -30954,35 +30574,6 @@ components:
|
||||||
* `success` - Success
|
* `success` - Success
|
||||||
* `waiting` - Waiting
|
* `waiting` - Waiting
|
||||||
* `invalid` - Invalid
|
* `invalid` - Invalid
|
||||||
EmailChallenge:
|
|
||||||
type: object
|
|
||||||
description: Email challenge
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
$ref: '#/components/schemas/ChallengeChoices'
|
|
||||||
flow_info:
|
|
||||||
$ref: '#/components/schemas/ContextualFlowInfo'
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
default: ak-stage-email
|
|
||||||
response_errors:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/ErrorDetail'
|
|
||||||
required:
|
|
||||||
- type
|
|
||||||
EmailChallengeResponseRequest:
|
|
||||||
type: object
|
|
||||||
description: |-
|
|
||||||
Email challenge resposen. No fields. This challenge is
|
|
||||||
always declared invalid to give the user a chance to retry
|
|
||||||
properties:
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
default: ak-stage-email
|
|
||||||
EmailStage:
|
EmailStage:
|
||||||
type: object
|
type: object
|
||||||
description: EmailStage Serializer
|
description: EmailStage Serializer
|
||||||
|
@ -31983,7 +31574,7 @@ components:
|
||||||
description: Get export URL for flow
|
description: Get export URL for flow
|
||||||
readOnly: true
|
readOnly: true
|
||||||
layout:
|
layout:
|
||||||
$ref: '#/components/schemas/LayoutEnum'
|
$ref: '#/components/schemas/FlowLayoutEnum'
|
||||||
denied_action:
|
denied_action:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/DeniedActionEnum'
|
- $ref: '#/components/schemas/DeniedActionEnum'
|
||||||
|
@ -32003,6 +31594,7 @@ components:
|
||||||
* `require_authenticated` - Require Authenticated
|
* `require_authenticated` - Require Authenticated
|
||||||
* `require_unauthenticated` - Require Unauthenticated
|
* `require_unauthenticated` - Require Unauthenticated
|
||||||
* `require_superuser` - Require Superuser
|
* `require_superuser` - Require Superuser
|
||||||
|
* `require_outpost` - Require Outpost
|
||||||
required:
|
required:
|
||||||
- background
|
- background
|
||||||
- cache_count
|
- cache_count
|
||||||
|
@ -32017,47 +31609,25 @@ components:
|
||||||
- title
|
- title
|
||||||
FlowChallengeResponseRequest:
|
FlowChallengeResponseRequest:
|
||||||
oneOf:
|
oneOf:
|
||||||
- $ref: '#/components/schemas/AppleChallengeResponseRequest'
|
|
||||||
- $ref: '#/components/schemas/AuthenticatorDuoChallengeResponseRequest'
|
- $ref: '#/components/schemas/AuthenticatorDuoChallengeResponseRequest'
|
||||||
- $ref: '#/components/schemas/AuthenticatorSMSChallengeResponseRequest'
|
|
||||||
- $ref: '#/components/schemas/AuthenticatorStaticChallengeResponseRequest'
|
|
||||||
- $ref: '#/components/schemas/AuthenticatorTOTPChallengeResponseRequest'
|
|
||||||
- $ref: '#/components/schemas/AuthenticatorValidationChallengeResponseRequest'
|
|
||||||
- $ref: '#/components/schemas/AuthenticatorWebAuthnChallengeResponseRequest'
|
|
||||||
- $ref: '#/components/schemas/AutoSubmitChallengeResponseRequest'
|
- $ref: '#/components/schemas/AutoSubmitChallengeResponseRequest'
|
||||||
- $ref: '#/components/schemas/CaptchaChallengeResponseRequest'
|
|
||||||
- $ref: '#/components/schemas/ConsentChallengeResponseRequest'
|
- $ref: '#/components/schemas/ConsentChallengeResponseRequest'
|
||||||
- $ref: '#/components/schemas/DummyChallengeResponseRequest'
|
|
||||||
- $ref: '#/components/schemas/EmailChallengeResponseRequest'
|
|
||||||
- $ref: '#/components/schemas/IdentificationChallengeResponseRequest'
|
|
||||||
- $ref: '#/components/schemas/OAuthDeviceCodeChallengeResponseRequest'
|
- $ref: '#/components/schemas/OAuthDeviceCodeChallengeResponseRequest'
|
||||||
- $ref: '#/components/schemas/OAuthDeviceCodeFinishChallengeResponseRequest'
|
- $ref: '#/components/schemas/OAuthDeviceCodeFinishChallengeResponseRequest'
|
||||||
- $ref: '#/components/schemas/PasswordChallengeResponseRequest'
|
- $ref: '#/components/schemas/PasswordChallengeResponseRequest'
|
||||||
- $ref: '#/components/schemas/PlexAuthenticationChallengeResponseRequest'
|
- $ref: '#/components/schemas/PlexAuthenticationChallengeResponseRequest'
|
||||||
- $ref: '#/components/schemas/PromptChallengeResponseRequest'
|
- $ref: '#/components/schemas/PromptChallengeResponseRequest'
|
||||||
- $ref: '#/components/schemas/UserLoginChallengeResponseRequest'
|
|
||||||
discriminator:
|
discriminator:
|
||||||
propertyName: component
|
propertyName: component
|
||||||
mapping:
|
mapping:
|
||||||
ak-source-oauth-apple: '#/components/schemas/AppleChallengeResponseRequest'
|
|
||||||
ak-stage-authenticator-duo: '#/components/schemas/AuthenticatorDuoChallengeResponseRequest'
|
ak-stage-authenticator-duo: '#/components/schemas/AuthenticatorDuoChallengeResponseRequest'
|
||||||
ak-stage-authenticator-sms: '#/components/schemas/AuthenticatorSMSChallengeResponseRequest'
|
|
||||||
ak-stage-authenticator-static: '#/components/schemas/AuthenticatorStaticChallengeResponseRequest'
|
|
||||||
ak-stage-authenticator-totp: '#/components/schemas/AuthenticatorTOTPChallengeResponseRequest'
|
|
||||||
ak-stage-authenticator-validate: '#/components/schemas/AuthenticatorValidationChallengeResponseRequest'
|
|
||||||
ak-stage-authenticator-webauthn: '#/components/schemas/AuthenticatorWebAuthnChallengeResponseRequest'
|
|
||||||
ak-stage-autosubmit: '#/components/schemas/AutoSubmitChallengeResponseRequest'
|
ak-stage-autosubmit: '#/components/schemas/AutoSubmitChallengeResponseRequest'
|
||||||
ak-stage-captcha: '#/components/schemas/CaptchaChallengeResponseRequest'
|
|
||||||
ak-stage-consent: '#/components/schemas/ConsentChallengeResponseRequest'
|
ak-stage-consent: '#/components/schemas/ConsentChallengeResponseRequest'
|
||||||
ak-stage-dummy: '#/components/schemas/DummyChallengeResponseRequest'
|
|
||||||
ak-stage-email: '#/components/schemas/EmailChallengeResponseRequest'
|
|
||||||
ak-stage-identification: '#/components/schemas/IdentificationChallengeResponseRequest'
|
|
||||||
ak-provider-oauth2-device-code: '#/components/schemas/OAuthDeviceCodeChallengeResponseRequest'
|
ak-provider-oauth2-device-code: '#/components/schemas/OAuthDeviceCodeChallengeResponseRequest'
|
||||||
ak-provider-oauth2-device-code-finish: '#/components/schemas/OAuthDeviceCodeFinishChallengeResponseRequest'
|
ak-provider-oauth2-device-code-finish: '#/components/schemas/OAuthDeviceCodeFinishChallengeResponseRequest'
|
||||||
ak-stage-password: '#/components/schemas/PasswordChallengeResponseRequest'
|
ak-stage-password: '#/components/schemas/PasswordChallengeResponseRequest'
|
||||||
ak-source-plex: '#/components/schemas/PlexAuthenticationChallengeResponseRequest'
|
ak-source-plex: '#/components/schemas/PlexAuthenticationChallengeResponseRequest'
|
||||||
ak-stage-prompt: '#/components/schemas/PromptChallengeResponseRequest'
|
ak-stage-prompt: '#/components/schemas/PromptChallengeResponseRequest'
|
||||||
ak-stage-user-login: '#/components/schemas/UserLoginChallengeResponseRequest'
|
|
||||||
FlowDesignationEnum:
|
FlowDesignationEnum:
|
||||||
enum:
|
enum:
|
||||||
- authentication
|
- authentication
|
||||||
|
@ -32170,6 +31740,20 @@ components:
|
||||||
- next_planned_stage
|
- next_planned_stage
|
||||||
- plan_context
|
- plan_context
|
||||||
- session_id
|
- session_id
|
||||||
|
FlowLayoutEnum:
|
||||||
|
enum:
|
||||||
|
- stacked
|
||||||
|
- content_left
|
||||||
|
- content_right
|
||||||
|
- sidebar_left
|
||||||
|
- sidebar_right
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
* `stacked` - STACKED
|
||||||
|
* `content_left` - CONTENT_LEFT
|
||||||
|
* `content_right` - CONTENT_RIGHT
|
||||||
|
* `sidebar_left` - SIDEBAR_LEFT
|
||||||
|
* `sidebar_right` - SIDEBAR_RIGHT
|
||||||
FlowRequest:
|
FlowRequest:
|
||||||
type: object
|
type: object
|
||||||
description: Flow Serializer
|
description: Flow Serializer
|
||||||
|
@ -32207,7 +31791,7 @@ components:
|
||||||
description: Enable compatibility mode, increases compatibility with password
|
description: Enable compatibility mode, increases compatibility with password
|
||||||
managers on mobile devices.
|
managers on mobile devices.
|
||||||
layout:
|
layout:
|
||||||
$ref: '#/components/schemas/LayoutEnum'
|
$ref: '#/components/schemas/FlowLayoutEnum'
|
||||||
denied_action:
|
denied_action:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/DeniedActionEnum'
|
- $ref: '#/components/schemas/DeniedActionEnum'
|
||||||
|
@ -32227,6 +31811,7 @@ components:
|
||||||
* `require_authenticated` - Require Authenticated
|
* `require_authenticated` - Require Authenticated
|
||||||
* `require_unauthenticated` - Require Unauthenticated
|
* `require_unauthenticated` - Require Unauthenticated
|
||||||
* `require_superuser` - Require Superuser
|
* `require_superuser` - Require Superuser
|
||||||
|
* `require_outpost` - Require Outpost
|
||||||
required:
|
required:
|
||||||
- designation
|
- designation
|
||||||
- name
|
- name
|
||||||
|
@ -32285,7 +31870,7 @@ components:
|
||||||
description: Get export URL for flow
|
description: Get export URL for flow
|
||||||
readOnly: true
|
readOnly: true
|
||||||
layout:
|
layout:
|
||||||
$ref: '#/components/schemas/LayoutEnum'
|
$ref: '#/components/schemas/FlowLayoutEnum'
|
||||||
denied_action:
|
denied_action:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/DeniedActionEnum'
|
- $ref: '#/components/schemas/DeniedActionEnum'
|
||||||
|
@ -32341,7 +31926,7 @@ components:
|
||||||
description: Enable compatibility mode, increases compatibility with password
|
description: Enable compatibility mode, increases compatibility with password
|
||||||
managers on mobile devices.
|
managers on mobile devices.
|
||||||
layout:
|
layout:
|
||||||
$ref: '#/components/schemas/LayoutEnum'
|
$ref: '#/components/schemas/FlowLayoutEnum'
|
||||||
denied_action:
|
denied_action:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/DeniedActionEnum'
|
- $ref: '#/components/schemas/DeniedActionEnum'
|
||||||
|
@ -32626,68 +32211,6 @@ components:
|
||||||
format: uuid
|
format: uuid
|
||||||
required:
|
required:
|
||||||
- name
|
- name
|
||||||
IdentificationChallenge:
|
|
||||||
type: object
|
|
||||||
description: Identification challenges with all UI elements
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
$ref: '#/components/schemas/ChallengeChoices'
|
|
||||||
flow_info:
|
|
||||||
$ref: '#/components/schemas/ContextualFlowInfo'
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
default: ak-stage-identification
|
|
||||||
response_errors:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/ErrorDetail'
|
|
||||||
user_fields:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
nullable: true
|
|
||||||
password_fields:
|
|
||||||
type: boolean
|
|
||||||
application_pre:
|
|
||||||
type: string
|
|
||||||
enroll_url:
|
|
||||||
type: string
|
|
||||||
recovery_url:
|
|
||||||
type: string
|
|
||||||
passwordless_url:
|
|
||||||
type: string
|
|
||||||
primary_action:
|
|
||||||
type: string
|
|
||||||
sources:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/LoginSource'
|
|
||||||
show_source_labels:
|
|
||||||
type: boolean
|
|
||||||
required:
|
|
||||||
- password_fields
|
|
||||||
- primary_action
|
|
||||||
- show_source_labels
|
|
||||||
- type
|
|
||||||
- user_fields
|
|
||||||
IdentificationChallengeResponseRequest:
|
|
||||||
type: object
|
|
||||||
description: Identification challenge
|
|
||||||
properties:
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
default: ak-stage-identification
|
|
||||||
uid_field:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
password:
|
|
||||||
type: string
|
|
||||||
nullable: true
|
|
||||||
required:
|
|
||||||
- uid_field
|
|
||||||
IdentificationStage:
|
IdentificationStage:
|
||||||
type: object
|
type: object
|
||||||
description: IdentificationStage Serializer
|
description: IdentificationStage Serializer
|
||||||
|
@ -33725,20 +33248,6 @@ components:
|
||||||
required:
|
required:
|
||||||
- is_running
|
- is_running
|
||||||
- tasks
|
- tasks
|
||||||
LayoutEnum:
|
|
||||||
enum:
|
|
||||||
- stacked
|
|
||||||
- content_left
|
|
||||||
- content_right
|
|
||||||
- sidebar_left
|
|
||||||
- sidebar_right
|
|
||||||
type: string
|
|
||||||
description: |-
|
|
||||||
* `stacked` - STACKED
|
|
||||||
* `content_left` - CONTENT_LEFT
|
|
||||||
* `content_right` - CONTENT_RIGHT
|
|
||||||
* `sidebar_left` - SIDEBAR_LEFT
|
|
||||||
* `sidebar_right` - SIDEBAR_RIGHT
|
|
||||||
License:
|
License:
|
||||||
type: object
|
type: object
|
||||||
description: License Serializer
|
description: License Serializer
|
||||||
|
@ -33833,17 +33342,6 @@ components:
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- link
|
- link
|
||||||
LoginChallengeTypes:
|
|
||||||
oneOf:
|
|
||||||
- $ref: '#/components/schemas/RedirectChallenge'
|
|
||||||
- $ref: '#/components/schemas/PlexAuthenticationChallenge'
|
|
||||||
- $ref: '#/components/schemas/AppleLoginChallenge'
|
|
||||||
discriminator:
|
|
||||||
propertyName: component
|
|
||||||
mapping:
|
|
||||||
xak-flow-redirect: '#/components/schemas/RedirectChallenge'
|
|
||||||
ak-source-plex: '#/components/schemas/PlexAuthenticationChallenge'
|
|
||||||
ak-source-oauth-apple: '#/components/schemas/AppleLoginChallenge'
|
|
||||||
LoginMetrics:
|
LoginMetrics:
|
||||||
type: object
|
type: object
|
||||||
description: Login Metrics per 1h
|
description: Login Metrics per 1h
|
||||||
|
@ -33867,20 +33365,6 @@ components:
|
||||||
- authorizations
|
- authorizations
|
||||||
- logins
|
- logins
|
||||||
- logins_failed
|
- logins_failed
|
||||||
LoginSource:
|
|
||||||
type: object
|
|
||||||
description: Serializer for Login buttons of sources
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
icon_url:
|
|
||||||
type: string
|
|
||||||
nullable: true
|
|
||||||
challenge:
|
|
||||||
$ref: '#/components/schemas/LoginChallengeTypes'
|
|
||||||
required:
|
|
||||||
- challenge
|
|
||||||
- name
|
|
||||||
Metadata:
|
Metadata:
|
||||||
type: object
|
type: object
|
||||||
description: Serializer for blueprint metadata
|
description: Serializer for blueprint metadata
|
||||||
|
@ -34690,7 +34174,9 @@ components:
|
||||||
starts with http it is returned as-is
|
starts with http it is returned as-is
|
||||||
readOnly: true
|
readOnly: true
|
||||||
provider_type:
|
provider_type:
|
||||||
$ref: '#/components/schemas/ProviderTypeEnum'
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/ProviderTypeEnum'
|
||||||
|
description: ''
|
||||||
request_token_url:
|
request_token_url:
|
||||||
type: string
|
type: string
|
||||||
nullable: true
|
nullable: true
|
||||||
|
@ -34788,7 +34274,9 @@ components:
|
||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
provider_type:
|
provider_type:
|
||||||
$ref: '#/components/schemas/ProviderTypeEnum'
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/ProviderTypeEnum'
|
||||||
|
description: ''
|
||||||
request_token_url:
|
request_token_url:
|
||||||
type: string
|
type: string
|
||||||
nullable: true
|
nullable: true
|
||||||
|
@ -37281,7 +36769,7 @@ components:
|
||||||
description: Enable compatibility mode, increases compatibility with password
|
description: Enable compatibility mode, increases compatibility with password
|
||||||
managers on mobile devices.
|
managers on mobile devices.
|
||||||
layout:
|
layout:
|
||||||
$ref: '#/components/schemas/LayoutEnum'
|
$ref: '#/components/schemas/FlowLayoutEnum'
|
||||||
denied_action:
|
denied_action:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/DeniedActionEnum'
|
- $ref: '#/components/schemas/DeniedActionEnum'
|
||||||
|
@ -37301,6 +36789,7 @@ components:
|
||||||
* `require_authenticated` - Require Authenticated
|
* `require_authenticated` - Require Authenticated
|
||||||
* `require_unauthenticated` - Require Unauthenticated
|
* `require_unauthenticated` - Require Unauthenticated
|
||||||
* `require_superuser` - Require Superuser
|
* `require_superuser` - Require Superuser
|
||||||
|
* `require_outpost` - Require Outpost
|
||||||
PatchedFlowStageBindingRequest:
|
PatchedFlowStageBindingRequest:
|
||||||
type: object
|
type: object
|
||||||
description: FlowStageBinding Serializer
|
description: FlowStageBinding Serializer
|
||||||
|
@ -37894,7 +37383,9 @@ components:
|
||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
provider_type:
|
provider_type:
|
||||||
$ref: '#/components/schemas/ProviderTypeEnum'
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/ProviderTypeEnum'
|
||||||
|
description: ''
|
||||||
request_token_url:
|
request_token_url:
|
||||||
type: string
|
type: string
|
||||||
nullable: true
|
nullable: true
|
||||||
|
@ -39866,35 +39357,8 @@ components:
|
||||||
- authorization_flow
|
- authorization_flow
|
||||||
- name
|
- name
|
||||||
ProviderTypeEnum:
|
ProviderTypeEnum:
|
||||||
enum:
|
enum: []
|
||||||
- apple
|
type: boolean
|
||||||
- azuread
|
|
||||||
- discord
|
|
||||||
- facebook
|
|
||||||
- github
|
|
||||||
- google
|
|
||||||
- mailcow
|
|
||||||
- openidconnect
|
|
||||||
- okta
|
|
||||||
- patreon
|
|
||||||
- reddit
|
|
||||||
- twitch
|
|
||||||
- twitter
|
|
||||||
type: string
|
|
||||||
description: |-
|
|
||||||
* `apple` - Apple
|
|
||||||
* `azuread` - Azure AD
|
|
||||||
* `discord` - Discord
|
|
||||||
* `facebook` - Facebook
|
|
||||||
* `github` - GitHub
|
|
||||||
* `google` - Google
|
|
||||||
* `mailcow` - Mailcow
|
|
||||||
* `openidconnect` - OpenID Connect
|
|
||||||
* `okta` - Okta
|
|
||||||
* `patreon` - Patreon
|
|
||||||
* `reddit` - Reddit
|
|
||||||
* `twitch` - Twitch
|
|
||||||
* `twitter` - Twitter
|
|
||||||
ProxyMode:
|
ProxyMode:
|
||||||
enum:
|
enum:
|
||||||
- proxy
|
- proxy
|
||||||
|
@ -41517,24 +40981,6 @@ components:
|
||||||
- expression
|
- expression
|
||||||
- name
|
- name
|
||||||
- scope_name
|
- scope_name
|
||||||
SelectableStage:
|
|
||||||
type: object
|
|
||||||
description: Serializer for stages which can be selected by users
|
|
||||||
properties:
|
|
||||||
pk:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
verbose_name:
|
|
||||||
type: string
|
|
||||||
meta_model_name:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- meta_model_name
|
|
||||||
- name
|
|
||||||
- pk
|
|
||||||
- verbose_name
|
|
||||||
ServiceConnection:
|
ServiceConnection:
|
||||||
type: object
|
type: object
|
||||||
description: ServiceConnection Serializer
|
description: ServiceConnection Serializer
|
||||||
|
@ -42702,43 +42148,6 @@ components:
|
||||||
additionalProperties: {}
|
additionalProperties: {}
|
||||||
required:
|
required:
|
||||||
- name
|
- name
|
||||||
UserLoginChallenge:
|
|
||||||
type: object
|
|
||||||
description: Empty challenge
|
|
||||||
properties:
|
|
||||||
type:
|
|
||||||
$ref: '#/components/schemas/ChallengeChoices'
|
|
||||||
flow_info:
|
|
||||||
$ref: '#/components/schemas/ContextualFlowInfo'
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
default: ak-stage-user-login
|
|
||||||
response_errors:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/ErrorDetail'
|
|
||||||
pending_user:
|
|
||||||
type: string
|
|
||||||
pending_user_avatar:
|
|
||||||
type: string
|
|
||||||
required:
|
|
||||||
- pending_user
|
|
||||||
- pending_user_avatar
|
|
||||||
- type
|
|
||||||
UserLoginChallengeResponseRequest:
|
|
||||||
type: object
|
|
||||||
description: User login challenge
|
|
||||||
properties:
|
|
||||||
component:
|
|
||||||
type: string
|
|
||||||
minLength: 1
|
|
||||||
default: ak-stage-user-login
|
|
||||||
remember_me:
|
|
||||||
type: boolean
|
|
||||||
required:
|
|
||||||
- remember_me
|
|
||||||
UserLoginStage:
|
UserLoginStage:
|
||||||
type: object
|
type: object
|
||||||
description: UserLoginStage Serializer
|
description: UserLoginStage Serializer
|
||||||
|
|
|
@ -7,13 +7,13 @@
|
||||||
"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.14.0",
|
"@typescript-eslint/eslint-plugin": "^6.15.0",
|
||||||
"@typescript-eslint/parser": "^6.14.0",
|
"@typescript-eslint/parser": "^6.15.0",
|
||||||
"@wdio/cli": "^8.26.1",
|
"@wdio/cli": "^8.26.3",
|
||||||
"@wdio/local-runner": "^8.26.1",
|
"@wdio/local-runner": "^8.26.3",
|
||||||
"@wdio/mocha-framework": "^8.24.12",
|
"@wdio/mocha-framework": "^8.26.3",
|
||||||
"@wdio/spec-reporter": "^8.24.12",
|
"@wdio/spec-reporter": "^8.26.3",
|
||||||
"eslint": "^8.55.0",
|
"eslint": "^8.56.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",
|
||||||
|
@ -382,9 +382,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "8.55.0",
|
"version": "8.56.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
|
||||||
"integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==",
|
"integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
|
||||||
"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.14.0",
|
"version": "6.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.15.0.tgz",
|
||||||
"integrity": "sha512-1ZJBykBCXaSHG94vMMKmiHoL0MhNHKSVlcHVYZNw+BKxufhqQVTOawNpwwI1P5nIFZ/4jLVop0mcY6mJJDFNaw==",
|
"integrity": "sha512-j5qoikQqPccq9QoBAupOP+CBu8BaJ8BLjaXSioDISeTZkVO3ig7oSIKh3H+rEpee7xCXtWwSB4KIL5l6hWZzpg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.5.1",
|
"@eslint-community/regexpp": "^4.5.1",
|
||||||
"@typescript-eslint/scope-manager": "6.14.0",
|
"@typescript-eslint/scope-manager": "6.15.0",
|
||||||
"@typescript-eslint/type-utils": "6.14.0",
|
"@typescript-eslint/type-utils": "6.15.0",
|
||||||
"@typescript-eslint/utils": "6.14.0",
|
"@typescript-eslint/utils": "6.15.0",
|
||||||
"@typescript-eslint/visitor-keys": "6.14.0",
|
"@typescript-eslint/visitor-keys": "6.15.0",
|
||||||
"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.14.0",
|
"version": "6.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.15.0.tgz",
|
||||||
"integrity": "sha512-QjToC14CKacd4Pa7JK4GeB/vHmWFJckec49FR4hmIRf97+KXole0T97xxu9IFiPxVQ1DBWrQ5wreLwAGwWAVQA==",
|
"integrity": "sha512-MkgKNnsjC6QwcMdlNAel24jjkEO/0hQaMDLqP4S9zq5HBAUJNQB6y+3DwLjX7b3l2b37eNAxMPLwb3/kh8VKdA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "6.14.0",
|
"@typescript-eslint/scope-manager": "6.15.0",
|
||||||
"@typescript-eslint/types": "6.14.0",
|
"@typescript-eslint/types": "6.15.0",
|
||||||
"@typescript-eslint/typescript-estree": "6.14.0",
|
"@typescript-eslint/typescript-estree": "6.15.0",
|
||||||
"@typescript-eslint/visitor-keys": "6.14.0",
|
"@typescript-eslint/visitor-keys": "6.15.0",
|
||||||
"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.14.0",
|
"version": "6.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.15.0.tgz",
|
||||||
"integrity": "sha512-VT7CFWHbZipPncAZtuALr9y3EuzY1b1t1AEkIq2bTXUPKw+pHoXflGNG5L+Gv6nKul1cz1VH8fz16IThIU0tdg==",
|
"integrity": "sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "6.14.0",
|
"@typescript-eslint/types": "6.15.0",
|
||||||
"@typescript-eslint/visitor-keys": "6.14.0"
|
"@typescript-eslint/visitor-keys": "6.15.0"
|
||||||
},
|
},
|
||||||
"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.14.0",
|
"version": "6.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.15.0.tgz",
|
||||||
"integrity": "sha512-x6OC9Q7HfYKqjnuNu5a7kffIYs3No30isapRBJl1iCHLitD8O0lFbRcVGiOcuyN837fqXzPZ1NS10maQzZMKqw==",
|
"integrity": "sha512-CnmHKTfX6450Bo49hPg2OkIm/D/TVYV7jO1MCfPYGwf6x3GO0VU8YMO5AYMn+u3X05lRRxA4fWCz87GFQV6yVQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/typescript-estree": "6.14.0",
|
"@typescript-eslint/typescript-estree": "6.15.0",
|
||||||
"@typescript-eslint/utils": "6.14.0",
|
"@typescript-eslint/utils": "6.15.0",
|
||||||
"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.14.0",
|
"version": "6.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.15.0.tgz",
|
||||||
"integrity": "sha512-uty9H2K4Xs8E47z3SnXEPRNDfsis8JO27amp2GNCnzGETEW3yTqEIVg5+AI7U276oGF/tw6ZA+UesxeQ104ceA==",
|
"integrity": "sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ==",
|
||||||
"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.14.0",
|
"version": "6.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.15.0.tgz",
|
||||||
"integrity": "sha512-yPkaLwK0yH2mZKFE/bXkPAkkFgOv15GJAUzgUVonAbv0Hr4PK/N2yaA/4XQbTZQdygiDkpt5DkxPELqHguNvyw==",
|
"integrity": "sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "6.14.0",
|
"@typescript-eslint/types": "6.15.0",
|
||||||
"@typescript-eslint/visitor-keys": "6.14.0",
|
"@typescript-eslint/visitor-keys": "6.15.0",
|
||||||
"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.14.0",
|
"version": "6.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.15.0.tgz",
|
||||||
"integrity": "sha512-XwRTnbvRr7Ey9a1NT6jqdKX8y/atWG+8fAIu3z73HSP8h06i3r/ClMhmaF/RGWGW1tHJEwij1uEg2GbEmPYvYg==",
|
"integrity": "sha512-eF82p0Wrrlt8fQSRL0bGXzK5nWPRV2dYQZdajcfzOD9+cQz9O7ugifrJxclB+xVOvWvagXfqS4Es7vpLP4augw==",
|
||||||
"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.14.0",
|
"@typescript-eslint/scope-manager": "6.15.0",
|
||||||
"@typescript-eslint/types": "6.14.0",
|
"@typescript-eslint/types": "6.15.0",
|
||||||
"@typescript-eslint/typescript-estree": "6.14.0",
|
"@typescript-eslint/typescript-estree": "6.15.0",
|
||||||
"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.14.0",
|
"version": "6.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.15.0.tgz",
|
||||||
"integrity": "sha512-fB5cw6GRhJUz03MrROVuj5Zm/Q+XWlVdIsFj+Zb1Hvqouc8t+XP2H5y53QYU/MGtd2dPg6/vJJlhoX3xc2ehfw==",
|
"integrity": "sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "6.14.0",
|
"@typescript-eslint/types": "6.15.0",
|
||||||
"eslint-visitor-keys": "^3.4.1"
|
"eslint-visitor-keys": "^3.4.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -1141,18 +1141,18 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/cli": {
|
"node_modules/@wdio/cli": {
|
||||||
"version": "8.26.1",
|
"version": "8.26.3",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.26.1.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.26.3.tgz",
|
||||||
"integrity": "sha512-KZ3MVyH4N6j0Gdy6RL6Wv0uf5OeggFe0WRpOwZFjjQpYVEV8IEuB4kDcw8ld7f3kp9YYQGabMAkGrO6tnz5T8w==",
|
"integrity": "sha512-wrq145sNBw4DrsF5GEK8TBxqVWln7GZpNpM5QeDqCcZzVHIqDud4f7nADgZGbR8dJ96NVfC3rby6wbeRQUA+eg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.1",
|
"@types/node": "^20.1.1",
|
||||||
"@wdio/config": "8.24.12",
|
"@wdio/config": "8.26.3",
|
||||||
"@wdio/globals": "8.26.1",
|
"@wdio/globals": "8.26.3",
|
||||||
"@wdio/logger": "8.24.12",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/protocols": "8.24.12",
|
"@wdio/protocols": "8.24.12",
|
||||||
"@wdio/types": "8.24.12",
|
"@wdio/types": "8.26.3",
|
||||||
"@wdio/utils": "8.24.12",
|
"@wdio/utils": "8.26.3",
|
||||||
"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",
|
||||||
|
@ -1167,7 +1167,7 @@
|
||||||
"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.26.1",
|
"webdriverio": "8.26.3",
|
||||||
"yargs": "^17.7.2"
|
"yargs": "^17.7.2"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -1190,14 +1190,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/config": {
|
"node_modules/@wdio/config": {
|
||||||
"version": "8.24.12",
|
"version": "8.26.3",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.24.12.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.26.3.tgz",
|
||||||
"integrity": "sha512-3HW7qG1rIHzOIybV6oHR1CqLghsN0G3Xzs90ZciGL8dYhtcLtYCHwuWmBw4mkaB5xViU4AmZDuj7ChiG8Cr6Qw==",
|
"integrity": "sha512-NWh2JXRSyP4gY+jeC79u0L3hSXW/s3rOWez4M6qAglT91fZTXFbIl1GM8lnZlCq03ye2qFPqYrZ+4tGNQj7YxQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wdio/logger": "8.24.12",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/types": "8.24.12",
|
"@wdio/types": "8.26.3",
|
||||||
"@wdio/utils": "8.24.12",
|
"@wdio/utils": "8.26.3",
|
||||||
"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",
|
||||||
|
@ -1208,29 +1208,29 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/globals": {
|
"node_modules/@wdio/globals": {
|
||||||
"version": "8.26.1",
|
"version": "8.26.3",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.26.1.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.26.3.tgz",
|
||||||
"integrity": "sha512-hNJ4mvSHzvAzDcBisaNgwRzJ2sLN4B/fno6VZcskgfYlg7UyWpVHigyxaddP2e1OeoxLL9pc2hKCtwgDr4UozA==",
|
"integrity": "sha512-RW3UsvnUb4DjxVOqIngXQMcDJlbH+QL/LeChznUF0FW+Mqg/mZWukBld5/dDwgQHk9F2TOzc8ctk5FM3s1AoWQ==",
|
||||||
"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.26.1"
|
"webdriverio": "8.26.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/local-runner": {
|
"node_modules/@wdio/local-runner": {
|
||||||
"version": "8.26.1",
|
"version": "8.26.3",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.26.1.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.26.3.tgz",
|
||||||
"integrity": "sha512-n7iiB/mKt7u6bd3uJTgqOTaN2r/EUVQpBMsrXyv5XidYEr9QHuq2OOE3biswdxSez24p0zZGU35fu6oUwz5J1Q==",
|
"integrity": "sha512-YWxTBp6tc8Dlz09rnRhV2GXV4b3w5G0WyYEf81D+kXDI3QxDvYn6QujByr6Q7fQ9yLwJU4ptnT2uL5IbAwVo2A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.0",
|
"@types/node": "^20.1.0",
|
||||||
"@wdio/logger": "8.24.12",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/repl": "8.24.12",
|
"@wdio/repl": "8.24.12",
|
||||||
"@wdio/runner": "8.26.1",
|
"@wdio/runner": "8.26.3",
|
||||||
"@wdio/types": "8.24.12",
|
"@wdio/types": "8.26.3",
|
||||||
"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"
|
||||||
|
@ -1267,16 +1267,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/mocha-framework": {
|
"node_modules/@wdio/mocha-framework": {
|
||||||
"version": "8.24.12",
|
"version": "8.26.3",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.24.12.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.26.3.tgz",
|
||||||
"integrity": "sha512-SHN7CYZnDkVUNYxLp8iMV92xcmU/4gq5dqA0pRrK4m5nIU7BoL0flm0kA+ydYUQyNedQh2ru1V63uNyTOyCKAg==",
|
"integrity": "sha512-r9uHUcXhh6TKFqTBCacnbtQx0nZjFsnV9Pony8CY9qcNCn2q666ucQbrtMfZdjuevhn5N0E710+El4eAvK3jyw==",
|
||||||
"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.24.12",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/types": "8.24.12",
|
"@wdio/types": "8.26.3",
|
||||||
"@wdio/utils": "8.24.12",
|
"@wdio/utils": "8.26.3",
|
||||||
"mocha": "^10.0.0"
|
"mocha": "^10.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -1302,14 +1302,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/reporter": {
|
"node_modules/@wdio/reporter": {
|
||||||
"version": "8.24.12",
|
"version": "8.26.3",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.24.12.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.26.3.tgz",
|
||||||
"integrity": "sha512-FtLzDTBXdgxXf4T9HJQ2bNpYYSKEw//jojFm9XzB4fPwzPeFY3HC+dbePucVW1SSLrVzVxqIOyHiwCLqQ/4cQw==",
|
"integrity": "sha512-F/sF1Hwxp1osM2wto4JydONQGxqkbOhwbLM0o4Y8eHPgK7/m+Kn9uygBDqPVpgQnpf0kUfhpICe9gaZzG4Jt+g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.0",
|
"@types/node": "^20.1.0",
|
||||||
"@wdio/logger": "8.24.12",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/types": "8.24.12",
|
"@wdio/types": "8.26.3",
|
||||||
"diff": "^5.0.0",
|
"diff": "^5.0.0",
|
||||||
"object-inspect": "^1.12.0"
|
"object-inspect": "^1.12.0"
|
||||||
},
|
},
|
||||||
|
@ -1318,35 +1318,35 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/runner": {
|
"node_modules/@wdio/runner": {
|
||||||
"version": "8.26.1",
|
"version": "8.26.3",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.26.1.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.26.3.tgz",
|
||||||
"integrity": "sha512-Uhz82HD53LvfW6hLwd/cX+vYVZpiKtAr7ipTxaLt5VPEIwcDlrVz1hoVkVMGyfvkAuHxDKLZx16bigfMg+5eTQ==",
|
"integrity": "sha512-mbZGkBbXTRtj1hL5QUbNxpJvhE4rkXvYlUuea1uOVk3e2/+k2dZeGeKPgh1Q7Dt07118dfujCB7pQCYldE/dGg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.0",
|
"@types/node": "^20.1.0",
|
||||||
"@wdio/config": "8.24.12",
|
"@wdio/config": "8.26.3",
|
||||||
"@wdio/globals": "8.26.1",
|
"@wdio/globals": "8.26.3",
|
||||||
"@wdio/logger": "8.24.12",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/types": "8.24.12",
|
"@wdio/types": "8.26.3",
|
||||||
"@wdio/utils": "8.24.12",
|
"@wdio/utils": "8.26.3",
|
||||||
"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.12",
|
"webdriver": "8.26.3",
|
||||||
"webdriverio": "8.26.1"
|
"webdriverio": "8.26.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.13 || >=18"
|
"node": "^16.13 || >=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/spec-reporter": {
|
"node_modules/@wdio/spec-reporter": {
|
||||||
"version": "8.24.12",
|
"version": "8.26.3",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.24.12.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.26.3.tgz",
|
||||||
"integrity": "sha512-Ng3ErWK8eESamCYwIr2Uv49+46RvmT8FnmGaJ6irJoAp101K8zENEs1pyqYHJReucN+ka/wM87blfc2k8NEHCA==",
|
"integrity": "sha512-YfKlBOmxGyJk08BpDnsjPsp85XyhG7Cu2qoAVxtJ8kkJOZaGfUg9TBV9DXDqvdZcxCMnPfDfQIda6LzfkZf58Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wdio/reporter": "8.24.12",
|
"@wdio/reporter": "8.26.3",
|
||||||
"@wdio/types": "8.24.12",
|
"@wdio/types": "8.26.3",
|
||||||
"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"
|
||||||
|
@ -1368,9 +1368,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/types": {
|
"node_modules/@wdio/types": {
|
||||||
"version": "8.24.12",
|
"version": "8.26.3",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.24.12.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.26.3.tgz",
|
||||||
"integrity": "sha512-SaD3OacDiW06DvSgAQ7sDBbpiI9qZRg7eoVYeBg3uSGVtUq84vTETRhhV7D6xTC00IqZu+mmN2TY5/q+7Gqy7w==",
|
"integrity": "sha512-WOxvSV4sKJ5QCRNTJHeKCzgO2TZmcK1eDlJ+FObt9Pnt+4pCRy/881eVY/Aj2bozn2hhzq0AK/h6oPAUV/gjCg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.0"
|
"@types/node": "^20.1.0"
|
||||||
|
@ -1380,14 +1380,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@wdio/utils": {
|
"node_modules/@wdio/utils": {
|
||||||
"version": "8.24.12",
|
"version": "8.26.3",
|
||||||
"resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.24.12.tgz",
|
"resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.26.3.tgz",
|
||||||
"integrity": "sha512-uzwZyBVgqz0Wz1KL3aOUaQsxT8TNkzxti4NNTSMrU256qAPqc/n75rB7V73QASapCMpy70mZZTsuPgQYYj4ytQ==",
|
"integrity": "sha512-LA/iCKgJQemAAXoN6vHyKBtngdkFUWEnjB8Yd1Xm3gUQTvY4GVlvcqOxC2RF5Th7/L2LNxc6TWuErYv/mm5H+w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@puppeteer/browsers": "^1.6.0",
|
"@puppeteer/browsers": "^1.6.0",
|
||||||
"@wdio/logger": "8.24.12",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/types": "8.24.12",
|
"@wdio/types": "8.26.3",
|
||||||
"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",
|
||||||
|
@ -2456,9 +2456,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/devtools-protocol": {
|
"node_modules/devtools-protocol": {
|
||||||
"version": "0.0.1233178",
|
"version": "0.0.1237913",
|
||||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1233178.tgz",
|
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1237913.tgz",
|
||||||
"integrity": "sha512-jmMfyaqlzddwmDaSR1AQ+5ek+f7rupZdxKuPdkRcoxrZoF70Idg/4dTgXA08TLPmwAwB54gh49Wm2l/gRM0eUg==",
|
"integrity": "sha512-Pxtmz2ZIqBkpU82HaIdsvCQBG94yTC4xajrEsWx9p38QKEfBCJktSazsHkrjf9j3dVVNPhg5LR21F6KWeXpjiQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/diff": {
|
"node_modules/diff": {
|
||||||
|
@ -2806,15 +2806,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "8.55.0",
|
"version": "8.56.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
|
||||||
"integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==",
|
"integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
|
||||||
"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.4",
|
"@eslint/eslintrc": "^2.1.4",
|
||||||
"@eslint/js": "8.55.0",
|
"@eslint/js": "8.56.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",
|
||||||
|
@ -3887,6 +3887,43 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/got": {
|
||||||
|
"version": "12.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz",
|
||||||
|
"integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==",
|
||||||
|
"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": ">=14.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",
|
||||||
|
@ -8458,18 +8495,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/webdriver": {
|
"node_modules/webdriver": {
|
||||||
"version": "8.24.12",
|
"version": "8.26.3",
|
||||||
"resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.24.12.tgz",
|
"resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.26.3.tgz",
|
||||||
"integrity": "sha512-03DQIClHoaAqTsmDkxGwo4HwHfkn9LzJ1wfNyUerzKg8DnyXeiT6ILqj6EXLfsvh5zddU2vhYGLFXSerPgkuOQ==",
|
"integrity": "sha512-vHbMj0BFXPMtKVmJsVIkFVbdOT8eXkjFeJ7LmJL8cMMe1S5Lt44DqRjSBBoGsqYoYgIBmKpqAQcDrHrv9m7smQ==",
|
||||||
"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.12",
|
"@wdio/config": "8.26.3",
|
||||||
"@wdio/logger": "8.24.12",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/protocols": "8.24.12",
|
"@wdio/protocols": "8.24.12",
|
||||||
"@wdio/types": "8.24.12",
|
"@wdio/types": "8.26.3",
|
||||||
"@wdio/utils": "8.24.12",
|
"@wdio/utils": "8.26.3",
|
||||||
"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",
|
||||||
|
@ -8479,61 +8516,24 @@
|
||||||
"node": "^16.13 || >=18"
|
"node": "^16.13 || >=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/webdriver/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/webdriver/node_modules/got": {
|
|
||||||
"version": "12.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz",
|
|
||||||
"integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==",
|
|
||||||
"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": ">=14.16"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sindresorhus/got?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/webdriverio": {
|
"node_modules/webdriverio": {
|
||||||
"version": "8.26.1",
|
"version": "8.26.3",
|
||||||
"resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.26.1.tgz",
|
"resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.26.3.tgz",
|
||||||
"integrity": "sha512-KnM92UPqk7FmPJpZf3krHrqn0ydjSdyAMn+i4uENxLBqm1OyQ12gSKtIatt8FOP9/C+UrFXATSOd+jRkU2xMkw==",
|
"integrity": "sha512-5Ka8MOQoK866EI3whiCvzD1IiKFBq9niWF3lh92uMt6ZjbUZZoe5esWIHhFsHFxT6dOOU8uXR/Gr6qsBJFZReA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.1.0",
|
"@types/node": "^20.1.0",
|
||||||
"@wdio/config": "8.24.12",
|
"@wdio/config": "8.26.3",
|
||||||
"@wdio/logger": "8.24.12",
|
"@wdio/logger": "8.24.12",
|
||||||
"@wdio/protocols": "8.24.12",
|
"@wdio/protocols": "8.24.12",
|
||||||
"@wdio/repl": "8.24.12",
|
"@wdio/repl": "8.24.12",
|
||||||
"@wdio/types": "8.24.12",
|
"@wdio/types": "8.26.3",
|
||||||
"@wdio/utils": "8.24.12",
|
"@wdio/utils": "8.26.3",
|
||||||
"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.1233178",
|
"devtools-protocol": "^0.0.1237913",
|
||||||
"grapheme-splitter": "^1.0.2",
|
"grapheme-splitter": "^1.0.2",
|
||||||
"import-meta-resolve": "^4.0.0",
|
"import-meta-resolve": "^4.0.0",
|
||||||
"is-plain-obj": "^4.1.0",
|
"is-plain-obj": "^4.1.0",
|
||||||
|
@ -8545,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.12"
|
"webdriver": "8.26.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.13 || >=18"
|
"node": "^16.13 || >=18"
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
"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.14.0",
|
"@typescript-eslint/eslint-plugin": "^6.15.0",
|
||||||
"@typescript-eslint/parser": "^6.14.0",
|
"@typescript-eslint/parser": "^6.15.0",
|
||||||
"@wdio/cli": "^8.26.1",
|
"@wdio/cli": "^8.26.3",
|
||||||
"@wdio/local-runner": "^8.26.1",
|
"@wdio/local-runner": "^8.26.3",
|
||||||
"@wdio/mocha-framework": "^8.24.12",
|
"@wdio/mocha-framework": "^8.26.3",
|
||||||
"@wdio/spec-reporter": "^8.24.12",
|
"@wdio/spec-reporter": "^8.26.3",
|
||||||
"eslint": "^8.55.0",
|
"eslint": "^8.56.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",
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
// THIS IS A GENERATED FILE. DO NOT EDIT BY HAND.
|
||||||
|
//
|
||||||
|
// This file is generated by the build-storybook-import-maps script in the UI's base directory.
|
||||||
|
// This is a *hack* to work around an inconsistency in the way rollup, vite, and storybook
|
||||||
|
// import CSS modules.
|
||||||
|
//
|
||||||
|
// Sometime around 2030 or so, the Javascript community may finally get its collective act together
|
||||||
|
// and we'll have one unified way of doing this. I can only hope.
|
||||||
|
|
||||||
|
export const cssImportMaps = {
|
||||||
|
'import AKGlobal from "@goauthentik/common/styles/authentik.css";':
|
||||||
|
'import AKGlobal from "@goauthentik/common/styles/authentik.css?inline";',
|
||||||
|
'import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";':
|
||||||
|
'import PFAlert from "@patternfly/patternfly/components/Alert/alert.css?inline";',
|
||||||
|
'import PFAlertGroup from "@patternfly/patternfly/components/AlertGroup/alert-group.css";':
|
||||||
|
'import PFAlertGroup from "@patternfly/patternfly/components/AlertGroup/alert-group.css?inline";',
|
||||||
|
'import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";':
|
||||||
|
'import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css?inline";',
|
||||||
|
'import PFBackdrop from "@patternfly/patternfly/components/Backdrop/backdrop.css";':
|
||||||
|
'import PFBackdrop from "@patternfly/patternfly/components/Backdrop/backdrop.css?inline";',
|
||||||
|
'import PFBackgroundImage from "@patternfly/patternfly/components/BackgroundImage/background-image.css";':
|
||||||
|
'import PFBackgroundImage from "@patternfly/patternfly/components/BackgroundImage/background-image.css?inline";',
|
||||||
|
'import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";':
|
||||||
|
'import PFBanner from "@patternfly/patternfly/components/Banner/banner.css?inline";',
|
||||||
|
'import PFBase from "@patternfly/patternfly/patternfly-base.css";':
|
||||||
|
'import PFBase from "@patternfly/patternfly/patternfly-base.css?inline";',
|
||||||
|
'import PFBrand from "@patternfly/patternfly/components/Brand/brand.css";':
|
||||||
|
'import PFBrand from "@patternfly/patternfly/components/Brand/brand.css?inline";',
|
||||||
|
'import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";':
|
||||||
|
'import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css?inline";',
|
||||||
|
'import PFButton from "@patternfly/patternfly/components/Button/button.css";':
|
||||||
|
'import PFButton from "@patternfly/patternfly/components/Button/button.css?inline";',
|
||||||
|
'import PFCard from "@patternfly/patternfly/components/Card/card.css";':
|
||||||
|
'import PFCard from "@patternfly/patternfly/components/Card/card.css?inline";',
|
||||||
|
'import PFCheck from "@patternfly/patternfly/components/Check/check.css";':
|
||||||
|
'import PFCheck from "@patternfly/patternfly/components/Check/check.css?inline";',
|
||||||
|
'import PFChip from "@patternfly/patternfly/components/Chip/chip.css";':
|
||||||
|
'import PFChip from "@patternfly/patternfly/components/Chip/chip.css?inline";',
|
||||||
|
'import PFChipGroup from "@patternfly/patternfly/components/ChipGroup/chip-group.css";':
|
||||||
|
'import PFChipGroup from "@patternfly/patternfly/components/ChipGroup/chip-group.css?inline";',
|
||||||
|
'import PFContent from "@patternfly/patternfly/components/Content/content.css";':
|
||||||
|
'import PFContent from "@patternfly/patternfly/components/Content/content.css?inline";',
|
||||||
|
'import PFDataList from "@patternfly/patternfly/components/DataList/data-list.css";':
|
||||||
|
'import PFDataList from "@patternfly/patternfly/components/DataList/data-list.css?inline";',
|
||||||
|
'import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";':
|
||||||
|
'import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css?inline";',
|
||||||
|
'import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";':
|
||||||
|
'import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css?inline";',
|
||||||
|
'import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";':
|
||||||
|
'import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css?inline";',
|
||||||
|
'import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";':
|
||||||
|
'import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css?inline";',
|
||||||
|
'import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";':
|
||||||
|
'import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css?inline";',
|
||||||
|
'import PFExpandableSection from "@patternfly/patternfly/components/ExpandableSection/expandable-section.css";':
|
||||||
|
'import PFExpandableSection from "@patternfly/patternfly/components/ExpandableSection/expandable-section.css?inline";',
|
||||||
|
'import PFFAIcons from "@patternfly/patternfly/base/patternfly-fa-icons.css";':
|
||||||
|
'import PFFAIcons from "@patternfly/patternfly/base/patternfly-fa-icons.css?inline";',
|
||||||
|
'import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css";':
|
||||||
|
'import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css?inline";',
|
||||||
|
'import PFForm from "@patternfly/patternfly/components/Form/form.css";':
|
||||||
|
'import PFForm from "@patternfly/patternfly/components/Form/form.css?inline";',
|
||||||
|
'import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";':
|
||||||
|
'import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css?inline";',
|
||||||
|
'import PFGallery from "@patternfly/patternfly/layouts/Gallery/gallery.css";':
|
||||||
|
'import PFGallery from "@patternfly/patternfly/layouts/Gallery/gallery.css?inline";',
|
||||||
|
'import PFGlobal from "@patternfly/patternfly/patternfly-base.css";':
|
||||||
|
'import PFGlobal from "@patternfly/patternfly/patternfly-base.css?inline";',
|
||||||
|
'import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";':
|
||||||
|
'import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css?inline";',
|
||||||
|
'import PFHint from "@patternfly/patternfly/components/Hint/hint.css";':
|
||||||
|
'import PFHint from "@patternfly/patternfly/components/Hint/hint.css?inline";',
|
||||||
|
'import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";':
|
||||||
|
'import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css?inline";',
|
||||||
|
'import PFLabel from "@patternfly/patternfly/components/Label/label.css";':
|
||||||
|
'import PFLabel from "@patternfly/patternfly/components/Label/label.css?inline";',
|
||||||
|
'import PFList from "@patternfly/patternfly/components/List/list.css";':
|
||||||
|
'import PFList from "@patternfly/patternfly/components/List/list.css?inline";',
|
||||||
|
'import PFLogin from "@patternfly/patternfly/components/Login/login.css";':
|
||||||
|
'import PFLogin from "@patternfly/patternfly/components/Login/login.css?inline";',
|
||||||
|
'import PFModalBox from "@patternfly/patternfly/components/ModalBox/modal-box.css";':
|
||||||
|
'import PFModalBox from "@patternfly/patternfly/components/ModalBox/modal-box.css?inline";',
|
||||||
|
'import PFNav from "@patternfly/patternfly/components/Nav/nav.css";':
|
||||||
|
'import PFNav from "@patternfly/patternfly/components/Nav/nav.css?inline";',
|
||||||
|
'import PFNotificationBadge from "@patternfly/patternfly/components/NotificationBadge/notification-badge.css";':
|
||||||
|
'import PFNotificationBadge from "@patternfly/patternfly/components/NotificationBadge/notification-badge.css?inline";',
|
||||||
|
'import PFNotificationDrawer from "@patternfly/patternfly/components/NotificationDrawer/notification-drawer.css";':
|
||||||
|
'import PFNotificationDrawer from "@patternfly/patternfly/components/NotificationDrawer/notification-drawer.css?inline";',
|
||||||
|
'import PFPage from "@patternfly/patternfly/components/Page/page.css";':
|
||||||
|
'import PFPage from "@patternfly/patternfly/components/Page/page.css?inline";',
|
||||||
|
'import PFPagination from "@patternfly/patternfly/components/Pagination/pagination.css";':
|
||||||
|
'import PFPagination from "@patternfly/patternfly/components/Pagination/pagination.css?inline";',
|
||||||
|
'import PFProgressStepper from "@patternfly/patternfly/components/ProgressStepper/progress-stepper.css";':
|
||||||
|
'import PFProgressStepper from "@patternfly/patternfly/components/ProgressStepper/progress-stepper.css?inline";',
|
||||||
|
'import PFRadio from "@patternfly/patternfly/components/Radio/radio.css";':
|
||||||
|
'import PFRadio from "@patternfly/patternfly/components/Radio/radio.css?inline";',
|
||||||
|
'import PFSelect from "@patternfly/patternfly/components/Select/select.css";':
|
||||||
|
'import PFSelect from "@patternfly/patternfly/components/Select/select.css?inline";',
|
||||||
|
'import PFSidebar from "@patternfly/patternfly/components/Sidebar/sidebar.css";':
|
||||||
|
'import PFSidebar from "@patternfly/patternfly/components/Sidebar/sidebar.css?inline";',
|
||||||
|
'import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css";':
|
||||||
|
'import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css?inline";',
|
||||||
|
'import PFSpacing from "@patternfly/patternfly/utilities/Spacing/spacing.css";':
|
||||||
|
'import PFSpacing from "@patternfly/patternfly/utilities/Spacing/spacing.css?inline";',
|
||||||
|
'import PFSpinner from "@patternfly/patternfly/components/Spinner/spinner.css";':
|
||||||
|
'import PFSpinner from "@patternfly/patternfly/components/Spinner/spinner.css?inline";',
|
||||||
|
'import PFStack from "@patternfly/patternfly/layouts/Stack/stack.css";':
|
||||||
|
'import PFStack from "@patternfly/patternfly/layouts/Stack/stack.css?inline";',
|
||||||
|
'import PFSwitch from "@patternfly/patternfly/components/Switch/switch.css";':
|
||||||
|
'import PFSwitch from "@patternfly/patternfly/components/Switch/switch.css?inline";',
|
||||||
|
'import PFTable from "@patternfly/patternfly/components/Table/table.css";':
|
||||||
|
'import PFTable from "@patternfly/patternfly/components/Table/table.css?inline";',
|
||||||
|
'import PFTabs from "@patternfly/patternfly/components/Tabs/tabs.css";':
|
||||||
|
'import PFTabs from "@patternfly/patternfly/components/Tabs/tabs.css?inline";',
|
||||||
|
'import PFTitle from "@patternfly/patternfly/components/Title/title.css";':
|
||||||
|
'import PFTitle from "@patternfly/patternfly/components/Title/title.css?inline";',
|
||||||
|
'import PFToggleGroup from "@patternfly/patternfly/components/ToggleGroup/toggle-group.css";':
|
||||||
|
'import PFToggleGroup from "@patternfly/patternfly/components/ToggleGroup/toggle-group.css?inline";',
|
||||||
|
'import PFToolbar from "@patternfly/patternfly/components/Toolbar/toolbar.css";':
|
||||||
|
'import PFToolbar from "@patternfly/patternfly/components/Toolbar/toolbar.css?inline";',
|
||||||
|
'import PFTreeView from "@patternfly/patternfly/components/TreeView/tree-view.css";':
|
||||||
|
'import PFTreeView from "@patternfly/patternfly/components/TreeView/tree-view.css?inline";',
|
||||||
|
'import PFWizard from "@patternfly/patternfly/components/Wizard/wizard.css";':
|
||||||
|
'import PFWizard from "@patternfly/patternfly/components/Wizard/wizard.css?inline";',
|
||||||
|
'import ThemeDark from "@goauthentik/common/styles/theme-dark.css";':
|
||||||
|
'import ThemeDark from "@goauthentik/common/styles/theme-dark.css?inline";',
|
||||||
|
'import styles from "./LibraryPageImpl.css";':
|
||||||
|
'import styles from "./LibraryPageImpl.css?inline";',
|
||||||
|
};
|
|
@ -1,9 +1,12 @@
|
||||||
import replace from "@rollup/plugin-replace";
|
import replace from "@rollup/plugin-replace";
|
||||||
import type { StorybookConfig } from "@storybook/web-components-vite";
|
import type { StorybookConfig } from "@storybook/web-components-vite";
|
||||||
import { cwd } from "process";
|
import { cwd } from "process";
|
||||||
|
import modify from "rollup-plugin-modify";
|
||||||
import postcssLit from "rollup-plugin-postcss-lit";
|
import postcssLit from "rollup-plugin-postcss-lit";
|
||||||
import tsconfigPaths from "vite-tsconfig-paths";
|
import tsconfigPaths from "vite-tsconfig-paths";
|
||||||
|
|
||||||
|
import { cssImportMaps } from "./css-import-maps";
|
||||||
|
|
||||||
export const isProdBuild = process.env.NODE_ENV === "production";
|
export const isProdBuild = process.env.NODE_ENV === "production";
|
||||||
export const apiBasePath = process.env.AK_API_BASE_PATH || "";
|
export const apiBasePath = process.env.AK_API_BASE_PATH || "";
|
||||||
|
|
||||||
|
@ -27,9 +30,7 @@ const config: StorybookConfig = {
|
||||||
return {
|
return {
|
||||||
...config,
|
...config,
|
||||||
plugins: [
|
plugins: [
|
||||||
...config.plugins,
|
modify(cssImportMaps),
|
||||||
postcssLit(),
|
|
||||||
tsconfigPaths(),
|
|
||||||
replace({
|
replace({
|
||||||
"process.env.NODE_ENV": JSON.stringify(
|
"process.env.NODE_ENV": JSON.stringify(
|
||||||
isProdBuild ? "production" : "development",
|
isProdBuild ? "production" : "development",
|
||||||
|
@ -38,6 +39,9 @@ const config: StorybookConfig = {
|
||||||
"process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath),
|
"process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath),
|
||||||
"preventAssignment": true,
|
"preventAssignment": true,
|
||||||
}),
|
}),
|
||||||
|
...config.plugins,
|
||||||
|
postcssLit(),
|
||||||
|
tsconfigPaths(),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -28,7 +28,10 @@
|
||||||
"tsc:execute": "tsc --noEmit -p .",
|
"tsc:execute": "tsc --noEmit -p .",
|
||||||
"tsc": "run-s build-locales tsc:execute",
|
"tsc": "run-s build-locales tsc:execute",
|
||||||
"storybook": "storybook dev -p 6006",
|
"storybook": "storybook dev -p 6006",
|
||||||
"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",
|
||||||
|
"storybook:build-import-map": "run-s storybook:build-import-map-script storybook:run-import-map-script",
|
||||||
|
"storybook:build-import-map-script": "cd scripts && tsc --esModuleInterop --module es2020 --target es2020 --moduleResolution 'node' build-storybook-import-maps.ts && mv build-storybook-import-maps.js build-storybook-import-maps.mjs",
|
||||||
|
"storybook:run-import-map-script": "node scripts/build-storybook-import-maps.mjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/lang-html": "^6.4.7",
|
"@codemirror/lang-html": "^6.4.7",
|
||||||
|
@ -39,7 +42,7 @@
|
||||||
"@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.5.1",
|
"@fortawesome/fontawesome-free": "^6.5.1",
|
||||||
"@goauthentik/api": "^2023.10.4-1701882394",
|
"@goauthentik/api": "^2023.10.4-1702989148",
|
||||||
"@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",
|
||||||
|
@ -83,26 +86,26 @@
|
||||||
"@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.6.4",
|
"@storybook/addon-essentials": "^7.6.5",
|
||||||
"@storybook/addon-links": "^7.6.4",
|
"@storybook/addon-links": "^7.6.5",
|
||||||
"@storybook/api": "^7.6.4",
|
"@storybook/api": "^7.6.5",
|
||||||
"@storybook/blocks": "^7.6.4",
|
"@storybook/blocks": "^7.6.4",
|
||||||
"@storybook/manager-api": "^7.6.4",
|
"@storybook/manager-api": "^7.6.5",
|
||||||
"@storybook/web-components": "^7.6.4",
|
"@storybook/web-components": "^7.6.5",
|
||||||
"@storybook/web-components-vite": "^7.6.4",
|
"@storybook/web-components-vite": "^7.6.5",
|
||||||
"@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.14.0",
|
"@typescript-eslint/eslint-plugin": "^6.15.0",
|
||||||
"@typescript-eslint/parser": "^6.14.0",
|
"@typescript-eslint/parser": "^6.15.0",
|
||||||
"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.55.0",
|
"eslint": "^8.56.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.11.0",
|
||||||
"eslint-plugin-sonarjs": "^0.23.0",
|
"eslint-plugin-sonarjs": "^0.23.0",
|
||||||
"eslint-plugin-storybook": "^0.6.15",
|
"eslint-plugin-storybook": "^0.6.15",
|
||||||
"lit-analyzer": "^2.0.2",
|
"lit-analyzer": "^2.0.2",
|
||||||
|
@ -112,11 +115,12 @@
|
||||||
"pyright": "=1.1.338",
|
"pyright": "=1.1.338",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"rollup": "^4.9.0",
|
"rollup": "^4.9.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-modify": "^3.0.0",
|
||||||
"rollup-plugin-postcss-lit": "^2.1.0",
|
"rollup-plugin-postcss-lit": "^2.1.0",
|
||||||
"storybook": "^7.6.4",
|
"storybook": "^7.6.5",
|
||||||
"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",
|
||||||
|
@ -125,9 +129,9 @@
|
||||||
"vite-tsconfig-paths": "^4.2.2"
|
"vite-tsconfig-paths": "^4.2.2"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@esbuild/darwin-arm64": "^0.19.9",
|
"@esbuild/darwin-arm64": "^0.19.10",
|
||||||
"@esbuild/linux-amd64": "^0.18.11",
|
"@esbuild/linux-amd64": "^0.18.11",
|
||||||
"@esbuild/linux-arm64": "^0.19.9"
|
"@esbuild/linux-arm64": "^0.19.10"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
|
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
function* walkFilesystem(dir: string): Generator<string, undefined, any> {
|
||||||
|
const openeddir = fs.opendirSync(dir);
|
||||||
|
if (!openeddir) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let d: fs.Dirent | null;
|
||||||
|
while ((d = openeddir?.readSync())) {
|
||||||
|
if (!d) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const entry = path.join(dir, d.name);
|
||||||
|
if (d.isDirectory()) yield* walkFilesystem(entry);
|
||||||
|
else if (d.isFile()) yield entry;
|
||||||
|
}
|
||||||
|
openeddir.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
const import_re = /^(import \w+ from .*\.css)";/;
|
||||||
|
function extractImportLinesFromFile(path: string) {
|
||||||
|
const source = fs.readFileSync(path, { encoding: "utf8", flag: "r" });
|
||||||
|
const lines = source?.split("\n") ?? [];
|
||||||
|
return lines.filter((l) => import_re.test(l));
|
||||||
|
}
|
||||||
|
|
||||||
|
function createOneImportLine(line: string) {
|
||||||
|
const importMatch = import_re.exec(line);
|
||||||
|
if (!importMatch) {
|
||||||
|
throw new Error("How did an unmatchable line get here?");
|
||||||
|
}
|
||||||
|
const importContent = importMatch[1];
|
||||||
|
if (!importContent) {
|
||||||
|
throw new Error("How did an unmatchable line get here!?");
|
||||||
|
}
|
||||||
|
return `'${importContent}";': '${importContent}?inline";',`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSourceFile = /\.ts$/;
|
||||||
|
function getTheSourceFiles() {
|
||||||
|
return Array.from(walkFilesystem(path.join(__dirname, "..", "src"))).filter((path) =>
|
||||||
|
isSourceFile.test(path),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTheImportLines(importPaths: string[]) {
|
||||||
|
const importLines: string[] = importPaths.reduce(
|
||||||
|
(acc: string[], path) => [...acc, extractImportLinesFromFile(path)].flat(),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const uniqueImportLines = new Set(importLines);
|
||||||
|
const sortedImportLines = Array.from(uniqueImportLines.keys());
|
||||||
|
sortedImportLines.sort();
|
||||||
|
return sortedImportLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
const importPaths = getTheSourceFiles();
|
||||||
|
const importLines = getTheImportLines(importPaths);
|
||||||
|
|
||||||
|
const outputFile = `
|
||||||
|
// THIS IS A GENERATED FILE. DO NOT EDIT BY HAND.
|
||||||
|
//
|
||||||
|
// This file is generated by the build-storybook-import-maps script in the UI's base directory.
|
||||||
|
// This is a *hack* to work around an inconsistency in the way rollup, vite, and storybook
|
||||||
|
// import CSS modules.
|
||||||
|
//
|
||||||
|
// Sometime around 2030 or so, the Javascript community may finally get its collective act together
|
||||||
|
// and we'll have one unified way of doing this. I can only hope.
|
||||||
|
|
||||||
|
export const cssImportMaps = {
|
||||||
|
${importLines.map(createOneImportLine).join("\n")}
|
||||||
|
};
|
||||||
|
`;
|
||||||
|
|
||||||
|
fs.writeFileSync(path.join(__dirname, "..", ".storybook", "css-import-maps.ts"), outputFile, {
|
||||||
|
encoding: "utf8",
|
||||||
|
flag: "w",
|
||||||
|
});
|
|
@ -18,8 +18,8 @@ import {
|
||||||
DeniedActionEnum,
|
DeniedActionEnum,
|
||||||
Flow,
|
Flow,
|
||||||
FlowDesignationEnum,
|
FlowDesignationEnum,
|
||||||
|
FlowLayoutEnum,
|
||||||
FlowsApi,
|
FlowsApi,
|
||||||
LayoutEnum,
|
|
||||||
PolicyEngineMode,
|
PolicyEngineMode,
|
||||||
} from "@goauthentik/api";
|
} from "@goauthentik/api";
|
||||||
|
|
||||||
|
@ -196,6 +196,13 @@ export class FlowForm extends ModelForm<Flow, string> {
|
||||||
>
|
>
|
||||||
${msg("Require superuser.")}
|
${msg("Require superuser.")}
|
||||||
</option>
|
</option>
|
||||||
|
<option
|
||||||
|
value=${AuthenticationEnum.RequireOutpost}
|
||||||
|
?selected=${this.instance?.authentication ===
|
||||||
|
AuthenticationEnum.RequireOutpost}
|
||||||
|
>
|
||||||
|
${msg("Require Outpost (flow can only be executed from an outpost).")}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<p class="pf-c-form__helper-text">
|
<p class="pf-c-form__helper-text">
|
||||||
${msg("Required authentication level for this flow.")}
|
${msg("Required authentication level for this flow.")}
|
||||||
|
@ -302,34 +309,34 @@ export class FlowForm extends ModelForm<Flow, string> {
|
||||||
>
|
>
|
||||||
<select class="pf-c-form-control">
|
<select class="pf-c-form-control">
|
||||||
<option
|
<option
|
||||||
value=${LayoutEnum.Stacked}
|
value=${FlowLayoutEnum.Stacked}
|
||||||
?selected=${this.instance?.layout === LayoutEnum.Stacked}
|
?selected=${this.instance?.layout === FlowLayoutEnum.Stacked}
|
||||||
>
|
>
|
||||||
${LayoutToLabel(LayoutEnum.Stacked)}
|
${LayoutToLabel(FlowLayoutEnum.Stacked)}
|
||||||
</option>
|
</option>
|
||||||
<option
|
<option
|
||||||
value=${LayoutEnum.ContentLeft}
|
value=${FlowLayoutEnum.ContentLeft}
|
||||||
?selected=${this.instance?.layout === LayoutEnum.ContentLeft}
|
?selected=${this.instance?.layout === FlowLayoutEnum.ContentLeft}
|
||||||
>
|
>
|
||||||
${LayoutToLabel(LayoutEnum.ContentLeft)}
|
${LayoutToLabel(FlowLayoutEnum.ContentLeft)}
|
||||||
</option>
|
</option>
|
||||||
<option
|
<option
|
||||||
value=${LayoutEnum.ContentRight}
|
value=${FlowLayoutEnum.ContentRight}
|
||||||
?selected=${this.instance?.layout === LayoutEnum.ContentRight}
|
?selected=${this.instance?.layout === FlowLayoutEnum.ContentRight}
|
||||||
>
|
>
|
||||||
${LayoutToLabel(LayoutEnum.ContentRight)}
|
${LayoutToLabel(FlowLayoutEnum.ContentRight)}
|
||||||
</option>
|
</option>
|
||||||
<option
|
<option
|
||||||
value=${LayoutEnum.SidebarLeft}
|
value=${FlowLayoutEnum.SidebarLeft}
|
||||||
?selected=${this.instance?.layout === LayoutEnum.SidebarLeft}
|
?selected=${this.instance?.layout === FlowLayoutEnum.SidebarLeft}
|
||||||
>
|
>
|
||||||
${LayoutToLabel(LayoutEnum.SidebarLeft)}
|
${LayoutToLabel(FlowLayoutEnum.SidebarLeft)}
|
||||||
</option>
|
</option>
|
||||||
<option
|
<option
|
||||||
value=${LayoutEnum.SidebarRight}
|
value=${FlowLayoutEnum.SidebarRight}
|
||||||
?selected=${this.instance?.layout === LayoutEnum.SidebarRight}
|
?selected=${this.instance?.layout === FlowLayoutEnum.SidebarRight}
|
||||||
>
|
>
|
||||||
${LayoutToLabel(LayoutEnum.SidebarRight)}
|
${LayoutToLabel(FlowLayoutEnum.SidebarRight)}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { msg } from "@lit/localize";
|
import { msg } from "@lit/localize";
|
||||||
|
|
||||||
import { Flow, FlowDesignationEnum, LayoutEnum } from "@goauthentik/api";
|
import { Flow, FlowDesignationEnum, FlowLayoutEnum } from "@goauthentik/api";
|
||||||
|
|
||||||
export function RenderFlowOption(flow: Flow): string {
|
export function RenderFlowOption(flow: Flow): string {
|
||||||
return `${flow.slug} (${flow.name})`;
|
return `${flow.slug} (${flow.name})`;
|
||||||
|
@ -27,19 +27,19 @@ export function DesignationToLabel(designation: FlowDesignationEnum): string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LayoutToLabel(layout: LayoutEnum): string {
|
export function LayoutToLabel(layout: FlowLayoutEnum): string {
|
||||||
switch (layout) {
|
switch (layout) {
|
||||||
case LayoutEnum.Stacked:
|
case FlowLayoutEnum.Stacked:
|
||||||
return msg("Stacked");
|
return msg("Stacked");
|
||||||
case LayoutEnum.ContentLeft:
|
case FlowLayoutEnum.ContentLeft:
|
||||||
return msg("Content left");
|
return msg("Content left");
|
||||||
case LayoutEnum.ContentRight:
|
case FlowLayoutEnum.ContentRight:
|
||||||
return msg("Content right");
|
return msg("Content right");
|
||||||
case LayoutEnum.SidebarLeft:
|
case FlowLayoutEnum.SidebarLeft:
|
||||||
return msg("Sidebar left");
|
return msg("Sidebar left");
|
||||||
case LayoutEnum.SidebarRight:
|
case FlowLayoutEnum.SidebarRight:
|
||||||
return msg("Sidebar right");
|
return msg("Sidebar right");
|
||||||
case LayoutEnum.UnknownDefaultOpenApi:
|
case FlowLayoutEnum.UnknownDefaultOpenApi:
|
||||||
return msg("Unknown layout");
|
return msg("Unknown layout");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,3 +124,27 @@ html > form > input {
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Flow-card adjustments for static pages */
|
||||||
|
.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;
|
||||||
|
max-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-height: 60rem) {
|
||||||
|
.pf-c-login.stacked .pf-c-login__main {
|
||||||
|
margin-top: 13rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -37,8 +37,8 @@ import {
|
||||||
ContextualFlowInfo,
|
ContextualFlowInfo,
|
||||||
FlowChallengeResponseRequest,
|
FlowChallengeResponseRequest,
|
||||||
FlowErrorChallenge,
|
FlowErrorChallenge,
|
||||||
|
FlowLayoutEnum,
|
||||||
FlowsApi,
|
FlowsApi,
|
||||||
LayoutEnum,
|
|
||||||
ResponseError,
|
ResponseError,
|
||||||
ShellChallenge,
|
ShellChallenge,
|
||||||
UiThemeEnum,
|
UiThemeEnum,
|
||||||
|
@ -115,9 +115,11 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
/* layouts */
|
/* layouts */
|
||||||
|
@media (min-height: 60rem) {
|
||||||
.pf-c-login.stacked .pf-c-login__main {
|
.pf-c-login.stacked .pf-c-login__main {
|
||||||
margin-top: 13rem;
|
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"
|
||||||
|
@ -451,7 +453,7 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
getLayout(): string {
|
getLayout(): string {
|
||||||
const prefilledFlow = globalAK()?.flow?.layout || LayoutEnum.Stacked;
|
const prefilledFlow = globalAK()?.flow?.layout || FlowLayoutEnum.Stacked;
|
||||||
if (this.challenge) {
|
if (this.challenge) {
|
||||||
return this.challenge?.flowInfo?.layout || prefilledFlow;
|
return this.challenge?.flowInfo?.layout || prefilledFlow;
|
||||||
}
|
}
|
||||||
|
@ -461,11 +463,11 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||||
getLayoutClass(): string {
|
getLayoutClass(): string {
|
||||||
const layout = this.getLayout();
|
const layout = this.getLayout();
|
||||||
switch (layout) {
|
switch (layout) {
|
||||||
case LayoutEnum.ContentLeft:
|
case FlowLayoutEnum.ContentLeft:
|
||||||
return "pf-c-login__container";
|
return "pf-c-login__container";
|
||||||
case LayoutEnum.ContentRight:
|
case FlowLayoutEnum.ContentRight:
|
||||||
return "pf-c-login__container content-right";
|
return "pf-c-login__container content-right";
|
||||||
case LayoutEnum.Stacked:
|
case FlowLayoutEnum.Stacked:
|
||||||
default:
|
default:
|
||||||
return "ak-login-container";
|
return "ak-login-container";
|
||||||
}
|
}
|
||||||
|
|
6896
web/xliff/de.xlf
6896
web/xliff/de.xlf
File diff suppressed because it is too large
Load Diff
10786
web/xliff/en.xlf
10786
web/xliff/en.xlf
File diff suppressed because it is too large
Load Diff
6734
web/xliff/es.xlf
6734
web/xliff/es.xlf
File diff suppressed because it is too large
Load Diff
12668
web/xliff/fr.xlf
12668
web/xliff/fr.xlf
File diff suppressed because it is too large
Load Diff
9657
web/xliff/pl.xlf
9657
web/xliff/pl.xlf
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
6727
web/xliff/tr.xlf
6727
web/xliff/tr.xlf
File diff suppressed because it is too large
Load Diff
12670
web/xliff/zh-Hans.xlf
12670
web/xliff/zh-Hans.xlf
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -8044,6 +8044,10 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||||
<trans-unit id="s7513372fe60f6387">
|
<trans-unit id="s7513372fe60f6387">
|
||||||
<source>Event volume</source>
|
<source>Event volume</source>
|
||||||
<target>事件容量</target>
|
<target>事件容量</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s047a5f0211fedc72">
|
||||||
|
<source>Require Outpost (flow can only be executed from an outpost).</source>
|
||||||
|
<target>需要前哨(流程只能从前哨执行)。</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
|
12533
web/xliff/zh_TW.xlf
12533
web/xliff/zh_TW.xlf
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,376 @@
|
||||||
|
---
|
||||||
|
title: SharePoint Server SE
|
||||||
|
---
|
||||||
|
|
||||||
|
<span class="badge badge--secondary">Support level: Community</span>
|
||||||
|
|
||||||
|
## What is Microsoft SharePoint
|
||||||
|
|
||||||
|
> SharePoint is a proprietary, web-based collaborative platform that integrates natively with Microsoft 365.
|
||||||
|
>
|
||||||
|
> Launched in 2001, SharePoint is primarily sold as a document management and storage system, although it is also used for sharing information through an intranet, implementing internal applications, and for implementing business processes.
|
||||||
|
>
|
||||||
|
> -- https://en.wikipedia.org/wiki/SharePoint
|
||||||
|
|
||||||
|
> Organizations use Microsoft SharePoint to create websites.
|
||||||
|
>
|
||||||
|
> You can use it as a secure place to store, organize, share, and access information from any device.
|
||||||
|
> All you need is a web browser, such as Microsoft Edge, Internet Explorer, Chrome, or Firefox.
|
||||||
|
>
|
||||||
|
> -- https://support.microsoft.com/en-us/office/what-is-sharepoint-97b915e6-651b-43b2-827d-fb25777f446f
|
||||||
|
|
||||||
|
:::note
|
||||||
|
There are many ways to implement SSO mechanism within Microsoft SharePoint Server Subscription Edition.
|
||||||
|
|
||||||
|
These guidelines provides the procedure to integrate authentik with an OIDC provider based on Microsoft documentation.
|
||||||
|
(cf. https://learn.microsoft.com/en-us/sharepoint/security-for-sharepoint-server/set-up-oidc-auth-in-sharepoint-server-with-msaad)
|
||||||
|
|
||||||
|
In addition, it provides the procedure to enable claims augmentations in order to resolve group memberships.
|
||||||
|
|
||||||
|
For all other integration models, read Microsoft official documentation.
|
||||||
|
(cf. https://learn.microsoft.com/en-us/sharepoint/security-for-sharepoint-server/plan-user-authentication)
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
This setup only works starting with **authentik** version **2023.10** and Microsoft **SharePoint** Subscription Edition starting with the **Cumulative Updates** of **September 2023**.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Preparation
|
||||||
|
|
||||||
|
When you configure OIDC with authentik, you need the following resources:
|
||||||
|
|
||||||
|
1. A SharePoint Server Subscription Edition farm starting with CU of September 2023
|
||||||
|
2. An authentik instance starting with version 2023.10
|
||||||
|
3. (Optional) [LDAPCP](https://www.ldapcp.com/docs/overview/introduction/) installed on the target SharePoint farm
|
||||||
|
|
||||||
|
:::info
|
||||||
|
Ensure that the authentik and SharePoint Server clocks are synchronized.
|
||||||
|
:::
|
||||||
|
|
||||||
|
These guidelines use the following placeholders for the overall setup:
|
||||||
|
|
||||||
|
| Name | Placeholder | Sample value |
|
||||||
|
| -------------------------------------------------- | ------------------------------------ | -------------------------------------------------------------------------------------- |
|
||||||
|
| authentik Application Name | `auth.applicationName` | SharePoint SE |
|
||||||
|
| authentik Application Slug | `auth.applicationSlug` | sharepoint-se |
|
||||||
|
| authentik OIDC Name | `auth.providerName` | OIDC-SP |
|
||||||
|
| authentik OIDC Configuration URL | `auth.providerConfigURL` | https://authentik.company/application/o/sharepoint-se/.well-known/openid-configuration |
|
||||||
|
| authentik OIDC Client ID | `auth.providerClientID` | 0ab1c234d567ef8a90123bc4567890e12fa3b45c |
|
||||||
|
| authentik OIDC Redirect URIs | `auth.providerRedirectURI` | https://sharepoint.company/.\* |
|
||||||
|
| (Optional) authentik LDAP Outpost URI | `ldap.outpostURI` | ak-outpost-ldap.authentik.svc.cluster.local |
|
||||||
|
| (Optional) authentik LDAP Service Account | `ldap.outpostServiceAccount` | cn=ldapservice,ou=users,dc=ldap,dc=goauthentik,dc=io |
|
||||||
|
| (Optional) authentik LDAP Service Account Password | `ldap.outpostServiceAccountPassword` | mystrongpassword |
|
||||||
|
| SharePoint Default Web Application URL | `sp.webAppURL` | https://sharepoint.company |
|
||||||
|
| SharePoint Trusted Token Issuer Name | `sp.issuerName` | Authentik |
|
||||||
|
| SharePoint Trusted Token Issuer Description | `sp.issuerDesc` | authentik IDP |
|
||||||
|
|
||||||
|
## authentik configuration
|
||||||
|
|
||||||
|
### Step 1: Create authentik OpenID Property Mappings
|
||||||
|
|
||||||
|
SharePoint requires additional properties within the OpenID and profile scopes in order to operate OIDC properly and be able to map incoming authentik OID Claims with Microsoft Claims.
|
||||||
|
|
||||||
|
Additional information from Microsoft documentation:
|
||||||
|
|
||||||
|
- https://learn.microsoft.com/en-us/entra/identity-platform/id-tokens#validate-tokens
|
||||||
|
- https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference#payload-claims
|
||||||
|
|
||||||
|
#### Add an OpenID scope mapping for SharePoint
|
||||||
|
|
||||||
|
From the authentik Admin Dashboard:
|
||||||
|
|
||||||
|
1. Open **Customisation > Property Mappings** page from the sidebar.
|
||||||
|
2. Click **Create** from the property mapping list command bar.
|
||||||
|
3. Within the new property mapping form, select **Scope Mapping**.
|
||||||
|
4. Click **Next** and enter the following values:
|
||||||
|
- **Name**: SPopenid
|
||||||
|
- **Scope name**: openid
|
||||||
|
- **Expression**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
return {
|
||||||
|
"nbf": "0", # Identifies the time before which the JWT can't be accepted for processing.
|
||||||
|
# 0 stand for the date 1970-01-01 in unix timestamp
|
||||||
|
"oid": user.uid, # This ID uniquely identifies the user across applications - two different applications signing in the same user receives the same value in the oid claim.
|
||||||
|
"upn": user.username # (Optional) User Principal Name, used for troubleshooting within JWT tokens or to setup SharePoint like ADFS
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Click **Finish**.
|
||||||
|
|
||||||
|
#### Add a profile scope mapping for SharePoint
|
||||||
|
|
||||||
|
From the authentik Admin Dashboard:
|
||||||
|
|
||||||
|
1. Open **Customisation > Property Mappings** page from the sidebar.
|
||||||
|
2. Click **Create** from the property mapping list command bar.
|
||||||
|
3. Within the new property mapping form, select **Scope Mapping**.
|
||||||
|
4. Click **Next** and enter the following values:
|
||||||
|
- **Name**: SPprofile
|
||||||
|
- **Scope name**: profile
|
||||||
|
- **Expression**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
return {
|
||||||
|
"name": request.user.name, # The name claim provides a human-readable value that identifies the subject of the token.
|
||||||
|
"given_name": request.user.name, # Interoperability with Microsoft Entra ID
|
||||||
|
"unique_name": request.user.name, # (Optional) Used for troubleshooting within JWT tokens or to setup SharePoint like ADFS
|
||||||
|
"preferred_username": request.user.username, # (Optional) The primary username that represents the user.
|
||||||
|
"nickname": request.user.username, # (Optional) Used for troubleshooting within JWT tokens or to setup SharePoint like ADFS
|
||||||
|
"roles": [group.name for group in request.user.ak_groups.all()], # The set of roles that were assigned to the user who is logging in.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Click **Finish**.
|
||||||
|
|
||||||
|
### Step 2: Create authentik Open ID Connect Provider
|
||||||
|
|
||||||
|
From the authentik Admin Dashboard:
|
||||||
|
|
||||||
|
1. Open **Applications > Providers** page from the sidebar.
|
||||||
|
2. Click **Create** from the provider list command bar.
|
||||||
|
3. Within the new provider form, select **OAuth2/OpenID Provider**.
|
||||||
|
4. Click **Next** and enter the following values:
|
||||||
|
- **Name**: `auth.providerName`
|
||||||
|
- **Authentication flow**: default-authentication-flow
|
||||||
|
- **Authorization flow**: default-provider-authorization-implicit-consent
|
||||||
|
:::note
|
||||||
|
use the explicit flow if user consents are required
|
||||||
|
:::
|
||||||
|
- **Redirect URIs / Origins**: `auth.providerRedirectURI`
|
||||||
|
- **Signing Key**: authentik Self-signed Certificate
|
||||||
|
:::note
|
||||||
|
The certificate is used for signing JWT tokens;, if you change it after the integration do not forget to update your SharePoint Trusted Certificate.
|
||||||
|
:::
|
||||||
|
- **Access code validity**: minutes=5
|
||||||
|
:::note
|
||||||
|
The minimum is 5 minutes, otherwise SharePoint backend might consider the access code expired.
|
||||||
|
:::
|
||||||
|
- **Access Token validity**: minutes=15
|
||||||
|
:::note
|
||||||
|
The minimum is 15 minutes, otherwise SharePoint backend will consider the access token expired.
|
||||||
|
:::
|
||||||
|
- **Scopes**: select default email, SPopenid and SPprofile
|
||||||
|
- **Subject mode**: Based on the User's hashed ID
|
||||||
|
5. Click **Finish**.
|
||||||
|
|
||||||
|
### Step 3: Create an application in authentik
|
||||||
|
|
||||||
|
From the authentik Admin Dashboard:
|
||||||
|
|
||||||
|
1. Open **Applications > Applications** page from the sidebar.
|
||||||
|
2. Click **Create** from the application list command bar.
|
||||||
|
3. Within the new application form, enter the following values:
|
||||||
|
- **Name**: `auth.applicationName`
|
||||||
|
- **Slug**: `auth.applicationSlug`
|
||||||
|
- **Provider**: `auth.providerName`
|
||||||
|
- (Optional) **Launch URL**: `sp.webAppURL`
|
||||||
|
- (Optional) **Icon**: https://res-1.cdn.office.net/files/fabric-cdn-prod_20221209.001/assets/brand-icons/product/svg/sharepoint_48x1.svg
|
||||||
|
4. Click **Create**.
|
||||||
|
|
||||||
|
### Step 4: Setup OIDC authentication in SharePoint Server
|
||||||
|
|
||||||
|
#### Pre-requisites
|
||||||
|
|
||||||
|
##### Update SharePoint farm properties
|
||||||
|
|
||||||
|
The following PowerShell script must be updated according to your environment and executed as **Farm Admin account** with **elevated privileges** on a SharePoint Server.
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
|
||||||
|
- Update placeholders
|
||||||
|
- Read all script's comments
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
```PowerShell
|
||||||
|
Add-PSSnapin microsoft.sharepoint.powershell
|
||||||
|
|
||||||
|
# Setup farm properties to work with OIDC
|
||||||
|
$cert = New-SelfSignedCertificate -CertStoreLocation Cert:\LocalMachine\My -Provider 'Microsoft Enhanced RSA and AES Cryptographic Provider' -Subject "CN=SharePoint Cookie Cert"
|
||||||
|
$rsaCert = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert)
|
||||||
|
$fileName = $rsaCert.key.UniqueName
|
||||||
|
|
||||||
|
#If you have multiple SharePoint servers in the farm, you need to export certificate by Export-PfxCertificate and import certificate to all other SharePoint servers in the farm by Import-PfxCertificate and apply the same permissions as below.
|
||||||
|
|
||||||
|
#After certificate is successfully imported to SharePoint Server, we will need to grant access permission to certificate private key.
|
||||||
|
|
||||||
|
$path = "$env:ALLUSERSPROFILE\Microsoft\Crypto\RSA\MachineKeys\$fileName"
|
||||||
|
$permissions = Get-Acl -Path $path
|
||||||
|
|
||||||
|
#Please replace the <web application pool account> with the real application pool account of your web application.
|
||||||
|
$access_rule = New-Object System.Security.AccessControl.FileSystemAccessRule("$($env:computername)\WSS_WPG", 'Read', 'None', 'None', 'Allow')
|
||||||
|
$permissions.AddAccessRule($access_rule)
|
||||||
|
Set-Acl -Path $path -AclObject $permissions
|
||||||
|
|
||||||
|
#Then we update farm properties only once.
|
||||||
|
$f = Get-SPFarm
|
||||||
|
$f.Farm.Properties['SP-NonceCookieCertificateThumbprint']=$cert.Thumbprint
|
||||||
|
$f.Farm.Properties['SP-NonceCookieHMACSecretKey']='seed'
|
||||||
|
$f.Farm.Update()
|
||||||
|
```
|
||||||
|
|
||||||
|
##### SharePoint settings in case of SSL offloading
|
||||||
|
|
||||||
|
Update the SharePoint farm to accept OAuth authentication over HTTP.
|
||||||
|
|
||||||
|
The following PowerShell script must be updated according to your environment and executed as **Farm Admin account** with **elevated privileges** on a SharePoint Server.
|
||||||
|
|
||||||
|
```PowerShell
|
||||||
|
Add-PSSnapin microsoft.sharepoint.powershell
|
||||||
|
$c = get-spsecuritytokenserviceconfig
|
||||||
|
$c.AllowOAuthOverHttp = $true
|
||||||
|
$c.update()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Create SharePoint authentication provider
|
||||||
|
|
||||||
|
The following PowerShell script must be updated according to your environment and executed as **Farm Admin account** with **elevated privileges** on a SharePoint Server.
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
|
||||||
|
- Update placeholders
|
||||||
|
- Read all script's comments.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
```PowerShell
|
||||||
|
Add-PSSnapin microsoft.sharepoint.powershell
|
||||||
|
|
||||||
|
# OIDC Settings
|
||||||
|
$metadataendpointurl = "auth.providerConfigURL"
|
||||||
|
$clientIdentifier = "auth.providerClientID"
|
||||||
|
$trustedTokenIssuerName = "sp.issuerName"
|
||||||
|
$trustedTokenIssuerDescription = "sp.issuerDesc"
|
||||||
|
|
||||||
|
# OIDC Claims Mapping
|
||||||
|
## Identity claim: oid => defined within the Authentik scope mapping
|
||||||
|
$idClaim = New-SPClaimTypeMapping "http://schemas.microsoft.com/identity/claims/objectidentifier" -IncomingClaimTypeDisplayName "oid" -SameAsIncoming
|
||||||
|
|
||||||
|
## User claims mappings
|
||||||
|
$claims = @(
|
||||||
|
$idClaim
|
||||||
|
## User Roles (Group membership)
|
||||||
|
,(New-SPClaimTypeMapping ([System.Security.Claims.ClaimTypes]::Role) -IncomingClaimTypeDisplayName "Role" -SameAsIncoming)
|
||||||
|
## User email
|
||||||
|
,(New-SPClaimTypeMapping ([System.Security.Claims.ClaimTypes]::Email) -IncomingClaimTypeDisplayName "Email" -SameAsIncoming)
|
||||||
|
## User given_name
|
||||||
|
,(New-SPClaimTypeMapping ([System.Security.Claims.ClaimTypes]::GivenName) -IncomingClaimTypeDisplayName "GivenName" -SameAsIncoming )
|
||||||
|
## (Optional) User account name
|
||||||
|
#,(New-SPClaimTypeMapping ([System.Security.Claims.ClaimTypes]::NameIdentifier) -IncomingClaimTypeDisplayName "Username" -SameAsIncoming)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
# Trust 3rd party identity token issuer
|
||||||
|
$trustedTokenIssuer = New-SPTrustedIdentityTokenIssuer -Name $trustedTokenIssuerName -Description $trustedTokenIssuerDescription -ClaimsMappings $claims -IdentifierClaim $idClaim.InputClaimType -DefaultClientIdentifier $clientIdentifier -MetadataEndPoint $metadataendpointurl -Scope "openid email profile"
|
||||||
|
#Note: Remove the profile scope if you plan to use the LDAPCP claims augmentation.
|
||||||
|
|
||||||
|
# Create the SharePoint authentication provider based on the trusted token issuer
|
||||||
|
New-SPAuthenticationProvider -TrustedIdentityTokenIssuer $trustedTokenIssuer
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Configure SharePoint web applications
|
||||||
|
|
||||||
|
From the Central Administration opened as a Farm Administrator:
|
||||||
|
|
||||||
|
1. Open the **Application Management > Manage web applications** page.
|
||||||
|
2. Select your web application `sp.webAppURL`.
|
||||||
|
3. Click **Authentication Providers** from the ribbon bar.
|
||||||
|
4. According to your environment, click on the target zone such as "Default".
|
||||||
|
5. Update the authentication provider form as following:
|
||||||
|
- Check **Trusted Identity Provider**
|
||||||
|
- Check the newly created provider named `sp.issuerName`
|
||||||
|
- (Optional) Set **Custom Sign In Page**: /\_trust/default.aspx
|
||||||
|
6. Click **Save**.
|
||||||
|
|
||||||
|
Repeat all steps for each target web applications that matches with `auth.providerRedirectURI`.
|
||||||
|
|
||||||
|
## (Optional) SharePoint enhancements
|
||||||
|
|
||||||
|
Objectives :
|
||||||
|
|
||||||
|
- Integrate SharePoint People Picker with authentik to search users and groups
|
||||||
|
- Augment SharePoint user claims at login stage
|
||||||
|
- Resolve user's membership
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
[LDAPCP](https://www.ldapcp.com/docs/overview/introduction/) must be installed on the target SharePoint farm.
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Step 1: Assign LDAPCP as claim provider for the identity token issuer
|
||||||
|
|
||||||
|
The following PowerShell script must be updated according to your environment and executed as **Farm Admin account** with **elevated privileges** on a SharePoint Server.
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
|
||||||
|
- Update placeholders
|
||||||
|
- Read all script's comments
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
```PowerShell
|
||||||
|
Add-PSSnapin microsoft.sharepoint.powershell
|
||||||
|
$trustedTokenIssuerName = "sp.issuerName"
|
||||||
|
|
||||||
|
$sptrust = Get-SPTrustedIdentityTokenIssuer $trustedTokenIssuerName
|
||||||
|
$sptrust.ClaimProviderName = "LDAPCP"
|
||||||
|
$sptrust.Update()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Configure LDAPCP claim types
|
||||||
|
|
||||||
|
From the SharePoint Central Administration opened as a Farm Administrator:
|
||||||
|
|
||||||
|
1. Open **Security > LDAPCP Configuration > Claim types configuration** page.
|
||||||
|
2. Update the mapping table to match these value:
|
||||||
|
|
||||||
|
| Claim type | Entity type | LDAP class | LDAP Attribute to query | LDAP attribute to display | PickerEntity metadata |
|
||||||
|
| ------------------------------------------------------------- | ----------- | ---------- | ----------------------- | ------------------------- | --------------------- |
|
||||||
|
| http://schemas.microsoft.com/identity/claims/objectidentifier | User | user | uid | sn | UserId |
|
||||||
|
| LDAP attribute linked to the main mapping for object User | User | user | mail | | Email |
|
||||||
|
| LDAP attribute linked to the main mapping for object User | User | user | sn | | DisplayName |
|
||||||
|
| http://schemas.microsoft.com/ws/2008/06/identity/claims/role | Group | group | cn | | DisplayName |
|
||||||
|
| LDAP attribute linked to the main mapping for object Group | Group | group | uid | | SPGroupID |
|
||||||
|
|
||||||
|
### Step 3: Create an authentik LDAP Outpost
|
||||||
|
|
||||||
|
From the authentik Admin Dashboard:
|
||||||
|
|
||||||
|
:::note
|
||||||
|
The following procedure apply to an authentik deployment within Kubernetes.
|
||||||
|
|
||||||
|
For other kinds of deployment, please refer to the [authentik documentation](https://goauthentik.io/docs/).
|
||||||
|
:::
|
||||||
|
|
||||||
|
1. Follow authentik [LDAP Provider Generic Setup](https://version-2023-10.goauthentik.io/docs/providers/ldap/generic_setup) with the following steps :
|
||||||
|
- **Create User/Group** to create a "service account" for `ldap.outpostServiceAccount` and a searchable group of users & groups
|
||||||
|
- **LDAP Flow** to create the authentication flow for the LDAP Provider
|
||||||
|
- **LDAP Provider** to create an LDAP provider which can be consumed by the LDAP Application
|
||||||
|
2. Open **Applications > Applications** page from the sidebar.
|
||||||
|
3. Open the edit form of your application `auth.applicationName`.
|
||||||
|
4. In the edit form:
|
||||||
|
- **Backchannel Providers**: add the LDAP provider previously created
|
||||||
|
5. Click **Update**.
|
||||||
|
|
||||||
|
### Step 4: Configure LDAPCP global configuration
|
||||||
|
|
||||||
|
From the SharePoint Central Administration opened as a Farm Administrator:
|
||||||
|
|
||||||
|
1. Open the **Security > LDAPCP Configuration > Global configuration** page.
|
||||||
|
2. Add an LDAP connection with th following properties:
|
||||||
|
- **LDAP Path**: LDAP://`ldap.outpostURI`/dc=ldap,dc=goauthentik,dc=io
|
||||||
|
- **Username**: `ldap.outpostServiceAccount`
|
||||||
|
- **Password**: `ldap.outpostServiceAccountPassword`
|
||||||
|
- **Authentication types**: check ServerBind
|
||||||
|
3. Augmentation - Check **Enable augmentation**
|
||||||
|
4. Augmentation - Select the Role claim "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
|
||||||
|
5. Augmentation - Check only "**Query this server**" for your `ldap.outpostURI`
|
||||||
|
6. User identifier properties:
|
||||||
|
- **LDAP class**: user
|
||||||
|
- **LDAP attribute**: uid
|
||||||
|
7. Display of user identifier results:
|
||||||
|
- Tick **Show the value of another LDAP attribute**: sn
|
||||||
|
8. Click on "**OK**"
|
||||||
|
|
||||||
|
_Note: The `ldap.outpostURI` should be the IP, hostname, or FQDN of the LDAP Outpost service deployed accessible by your SharePoint farm_.
|
Binary file not shown.
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 88 KiB |
|
@ -38,10 +38,9 @@ Use the following settings:
|
||||||
- Consumer key: `*Application (client) ID* value from above`
|
- Consumer key: `*Application (client) ID* value from above`
|
||||||
- Consumer secret: `*Value* of the secret from above`
|
- Consumer secret: `*Value* of the secret from above`
|
||||||
|
|
||||||
If you kept the default _Supported account types_ selection of _Single tenant_, then you must change the URLs below as well:
|
If you kept the default _Supported account types_ selection of _Single tenant_, then you must change the URL below as well:
|
||||||
|
|
||||||
- Authorization URL: `https://login.microsoftonline.com/*Directory (tenant) ID* from above/oauth2/v2.0/authorize`
|
- OIDC Well-known URL: `https://login.microsoftonline.com/*Directory (tenant) ID* from above/v2.0/.well-known/openid-configuration`
|
||||||
- Access token URL: `https://login.microsoftonline.com/*Directory (tenant) ID* from above/oauth2/v2.0/token`
|
|
||||||
|
|
||||||
![](./authentik_01.png)
|
![](./authentik_01.png)
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
"disqus-react": "^1.1.5",
|
"disqus-react": "^1.1.5",
|
||||||
"postcss": "^8.4.32",
|
"postcss": "^8.4.32",
|
||||||
"prism-react-renderer": "^2.3.0",
|
"prism-react-renderer": "^2.3.1",
|
||||||
"rapidoc": "^9.3.4",
|
"rapidoc": "^9.3.4",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-before-after-slider-component": "^1.1.8",
|
"react-before-after-slider-component": "^1.1.8",
|
||||||
|
@ -13786,9 +13786,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prism-react-renderer": {
|
"node_modules/prism-react-renderer": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.3.1.tgz",
|
||||||
"integrity": "sha512-UYRg2TkVIaI6tRVHC5OJ4/BxqPUxJkJvq/odLT/ykpt1zGYXooNperUxQcCvi87LyRnR4nCh81ceOA+e7nrydg==",
|
"integrity": "sha512-Rdf+HzBLR7KYjzpJ1rSoxT9ioO85nZngQEoFIhL07XhtJHlCU3SOz0GJ6+qvMyQe0Se+BV3qpe6Yd/NmQF5Juw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prismjs": "^1.26.0",
|
"@types/prismjs": "^1.26.0",
|
||||||
"clsx": "^2.0.0"
|
"clsx": "^2.0.0"
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
"disqus-react": "^1.1.5",
|
"disqus-react": "^1.1.5",
|
||||||
"postcss": "^8.4.32",
|
"postcss": "^8.4.32",
|
||||||
"prism-react-renderer": "^2.3.0",
|
"prism-react-renderer": "^2.3.1",
|
||||||
"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",
|
||||||
|
|
|
@ -32,6 +32,7 @@ module.exports = {
|
||||||
"services/paperless-ng/index",
|
"services/paperless-ng/index",
|
||||||
"services/rocketchat/index",
|
"services/rocketchat/index",
|
||||||
"services/roundcube/index",
|
"services/roundcube/index",
|
||||||
|
"services/sharepoint-se/index",
|
||||||
"services/vikunja/index",
|
"services/vikunja/index",
|
||||||
"services/wekan/index",
|
"services/wekan/index",
|
||||||
"services/wiki-js/index",
|
"services/wiki-js/index",
|
||||||
|
|
Reference in New Issue