Compare commits

..

26 Commits

Author SHA1 Message Date
f5580d311d release: 2024.8.1 2024-09-07 16:14:54 +02:00
99d292bce0 web/users: show - if device was registered before we started saving the time (cherry-pick #11256) (#11257)
web/users: show - if device was registered before we started saving the time (#11256)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-09-06 21:13:03 +02:00
b2801641bc internal: fix go paginator not setting page correctly (cherry-pick #11253) (#11255)
internal: fix go paginator not setting page correctly (#11253)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-09-06 18:46:18 +02:00
bfaa1046b2 core: fix missing argument name escaping for property mapping (cherry-pick #11231) (#11252)
core: fix missing argument name escaping for property mapping (#11231)

* escape property mapping args



* improve display of error



* fix error handling, missing dry_run argument



* use different sanitisation



* update docs



---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-09-06 16:47:27 +02:00
95c30400cc providers/ldap: rework search_group migration to work with read replicas (cherry-pick #11228) (#11229)
providers/ldap: rework search_group migration to work with read replicas (#11228)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-09-05 15:57:01 +02:00
e77480ee1d web/admin: improve error handling (cherry-pick #11212) (#11219)
web/admin: improve error handling (#11212)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-09-05 13:48:28 +02:00
905800e535 providers/ldap: fix incorrect permission check for search access (cherry-pick #11217) (#11218)
providers/ldap: fix incorrect permission check for search access (#11217)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-09-05 01:30:48 +02:00
fadeaef4c6 web/admin: fix missing Sync object button SCIM Provider (cherry-pick #11211) (#11213)
web/admin: fix missing Sync object button SCIM Provider (#11211)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-09-04 21:34:34 +02:00
437efda649 website/docs: add note about terraform provider (cherry-pick #11206) (#11208)
website/docs: add note about terraform provider (#11206)

* website/docs: add note about terraform provider



* Update website/docs/releases/2024/v2024.8.md



---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Jens L. <jens@goauthentik.io>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2024-09-04 19:50:00 +02:00
dd75d5f54b web/admin: fix misc dual select on different forms (#11203)
* fix prompt stage

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix identification stage

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix OAuth JWKS sources

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix oauth provider default scopes

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix outpost form

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix webauthn

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix transport form

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
# Conflicts:
#	web/src/admin/applications/wizard/methods/oauth/ak-application-wizard-authentication-by-oauth.ts
#	web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts
2024-09-04 13:46:45 +02:00
392a2e582e core: bump cryptography from 43.0.0 to 43.0.1 (cherry-pick #11185) (#11202)
core: bump cryptography from 43.0.0 to 43.0.1 (#11185)

Bumps [cryptography](https://github.com/pyca/cryptography) from 43.0.0 to 43.0.1.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/43.0.0...43.0.1)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-04 12:27:54 +02:00
a1da183721 root: backport s3 storage changes (cherry-pick #11181) (#11183)
root: backport s3 storage changes (#11181)

re-add _strip_signing_parameters
removed in https://github.com/jschneier/django-storages/pull/1402
could probably be re-factored to use the same approach that PR uses

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-09-03 22:08:55 +02:00
feea2df0b1 core: fix change_user_type always requiring usernames (cherry-pick #11177) (#11178)
core: fix change_user_type always requiring usernames (#11177)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-09-03 19:09:53 +02:00
b47acd8c76 web/admin: fix error in Outpost creation form (cherry-pick #11173) (#11175)
web/admin: fix error in Outpost creation form (#11173)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-09-03 18:26:37 +02:00
6fd87d9ced providers/ldap: fix migration assuming search group is set (cherry-pick #11170) (#11172)
providers/ldap: fix migration assuming search group is set (#11170)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-09-03 16:27:06 +02:00
acbb065808 website/docs: update release notes (#11151)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
# Conflicts:
#	website/docs/releases/2024/v2024.8.md
2024-09-03 14:05:18 +02:00
2fb097061d release: 2024.8.0 2024-09-02 14:14:03 +02:00
8962d17e03 web: fix dual-select with dynamic selection (cherry-pick #11133) (#11134)
web: fix dual-select with dynamic selection (#11133)

* web: fix dual-select with dynamic selection

For dynamic selection, the property name is `.selector` to message that it's a function the
API layer uses to select the elements.

A few bits of lint picked.

* web: added comment to clarify what the fallback selector does

Co-authored-by: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com>
2024-08-30 19:07:36 +02:00
8326e1490c ci: fix failing release attestation (cherry-pick #11107) (#11120)
ci: fix failing release attestation (#11107)

* ci: fix failing release attestation



* fix



---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-08-29 13:29:47 +02:00
091e4d3e4c enterprise: fix incorrect comparison for latest validity date (cherry-pick #11109) (#11110)
enterprise: fix incorrect comparison for latest validity date (#11109)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-08-29 01:58:56 +02:00
6ee77edcbb website/docs: 2024.8 release notes: reword group sync disable and fix typo (cherry-pick #11103) (#11108)
website/docs: 2024.8 release notes: reword group sync disable and fix… (#11103)

Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2024-08-29 01:34:33 +02:00
763e2288bf release: 2024.8.0-rc2 2024-08-28 20:22:52 +02:00
9cdb177ca7 website/docs: a couple of minor rewrite things (#11099)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
# Conflicts:
#	website/docs/releases/2024/v2024.8.md
2024-08-28 20:22:21 +02:00
6070508058 providers/oauth2: audit_ignore last_login change for generated service account (cherry-pick #11085) (#11086)
providers/oauth2: audit_ignore last_login change for generated service account (#11085)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-08-27 14:32:17 +02:00
ec13a5d84d release: 2024.8.0-rc1 2024-08-26 16:34:53 +02:00
057de82b01 schemas: fix XML Schema loading...for some reason?
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-08-26 16:34:47 +02:00
127 changed files with 6350 additions and 4172 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 2024.8.0
current_version = 2024.8.1
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?

View File

@ -205,7 +205,7 @@ gen: gen-build gen-client-ts
web-build: web-install ## Build the Authentik UI
cd web && npm run build
web: web-lint-fix web-lint web-check-compile web-test ## Automatically fix formatting issues in the Authentik UI source code, lint the code, and compile it
web: web-lint-fix web-lint web-check-compile ## Automatically fix formatting issues in the Authentik UI source code, lint the code, and compile it
web-install: ## Install the necessary libraries to build the Authentik UI
cd web && npm ci

View File

@ -2,7 +2,7 @@
from os import environ
__version__ = "2024.8.0"
__version__ = "2024.8.1"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -25,31 +25,3 @@ class BrandMiddleware:
if locale != "":
activate(locale)
return self.get_response(request)
class BrandCORSAPIMiddleware:
"""CORS for API requests depending on Brand"""
get_response: Callable[[HttpRequest], HttpResponse]
def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]):
self.get_response = get_response
def set_headers(self, request: HttpRequest, response: HttpResponse):
response["Access-Control-Allow-Origin"] = "http://localhost:8080"
response["Access-Control-Allow-Credentials"] = "true"
def __call__(self, request: HttpRequest) -> HttpResponse:
if request.method == "OPTIONS":
response = HttpResponse(
status=200,
)
self.set_headers(request, response)
response["Access-Control-Allow-Headers"] = (
"authorization,sentry-trace,x-authentik-csrf,content-type"
)
response["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS"
return response
response = self.get_response(request)
self.set_headers(request, response)
return response

View File

@ -30,8 +30,10 @@ from authentik.core.api.utils import (
PassiveSerializer,
)
from authentik.core.expression.evaluator import PropertyMappingEvaluator
from authentik.core.expression.exceptions import PropertyMappingExpressionException
from authentik.core.models import Group, PropertyMapping, User
from authentik.events.utils import sanitize_item
from authentik.lib.utils.errors import exception_to_string
from authentik.policies.api.exec import PolicyTestSerializer
from authentik.rbac.decorators import permission_required
@ -162,12 +164,15 @@ class PropertyMappingViewSet(
response_data = {"successful": True, "result": ""}
try:
result = mapping.evaluate(**context)
result = mapping.evaluate(dry_run=True, **context)
response_data["result"] = dumps(
sanitize_item(result), indent=(4 if format_result else None)
)
except PropertyMappingExpressionException as exc:
response_data["result"] = exception_to_string(exc.exc)
response_data["successful"] = False
except Exception as exc:
response_data["result"] = str(exc)
response_data["result"] = exception_to_string(exc)
response_data["successful"] = False
response = PropertyMappingTestResultSerializer(response_data)
return Response(response.data)

View File

@ -901,7 +901,7 @@ class PropertyMapping(SerializerModel, ManagedModel):
except ControlFlowException as exc:
raise exc
except Exception as exc:
raise PropertyMappingExpressionException(self, exc) from exc
raise PropertyMappingExpressionException(exc, self) from exc
def __str__(self):
return f"Property Mapping {self.name}"

View File

@ -13,7 +13,6 @@
<link rel="shortcut icon" href="{{ brand.branding_favicon }}">
{% block head_before %}
{% endblock %}
<link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly-base.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'dist/custom.css' %}" data-inject>
{% versioned_script "dist/poly-%v.js" %}

View File

@ -16,14 +16,12 @@ from django.views.decorators.clickjacking import xframe_options_sameorigin
from django.views.generic import View
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter, PolymorphicProxySerializer, extend_schema
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.permissions import AllowAny
from rest_framework.views import APIView
from sentry_sdk import capture_exception, start_span
from sentry_sdk.api import set_tag
from structlog.stdlib import BoundLogger, get_logger
from authentik.api.authentication import bearer_auth, get_authorization_header
from authentik.brands.models import Brand
from authentik.core.models import Application
from authentik.events.models import Event, EventAction, cleanse_dict
@ -118,14 +116,6 @@ class FlowExecutorView(APIView):
super().setup(request, flow_slug=flow_slug)
self.flow = get_object_or_404(Flow.objects.select_related(), slug=flow_slug)
self._logger = get_logger().bind(flow_slug=flow_slug)
# Usually flows are authenticated by session, we don't really use rest_framework's
# authentication method.
try:
user = bearer_auth(get_authorization_header(request))
if user:
request.user = user
except AuthenticationFailed:
pass
set_tag("authentik.flow", self.flow.slug)
def handle_invalid_flow(self, exc: FlowNonApplicableException) -> HttpResponse:

View File

@ -2,7 +2,6 @@
import re
import socket
from collections.abc import Iterable
from ipaddress import ip_address, ip_network
from textwrap import indent
from types import CodeType
@ -28,6 +27,12 @@ from authentik.stages.authenticator import devices_for_user
LOGGER = get_logger()
ARG_SANITIZE = re.compile(r"[:.-]")
def sanitize_arg(arg_name: str) -> str:
return re.sub(ARG_SANITIZE, "_", arg_name)
class BaseEvaluator:
"""Validate and evaluate python-based expressions"""
@ -177,9 +182,9 @@ class BaseEvaluator:
proc = PolicyProcess(PolicyBinding(policy=policy), request=req, connection=None)
return proc.profiling_wrapper()
def wrap_expression(self, expression: str, params: Iterable[str]) -> str:
def wrap_expression(self, expression: str) -> str:
"""Wrap expression in a function, call it, and save the result as `result`"""
handler_signature = ",".join(params)
handler_signature = ",".join(sanitize_arg(x) for x in self._context.keys())
full_expression = ""
full_expression += f"def handler({handler_signature}):\n"
full_expression += indent(expression, " ")
@ -188,8 +193,8 @@ class BaseEvaluator:
def compile(self, expression: str) -> CodeType:
"""Parse expression. Raises SyntaxError or ValueError if the syntax is incorrect."""
param_keys = self._context.keys()
return compile(self.wrap_expression(expression, param_keys), self._filename, "exec")
expression = self.wrap_expression(expression)
return compile(expression, self._filename, "exec")
def evaluate(self, expression_source: str) -> Any:
"""Parse and evaluate expression. If the syntax is incorrect, a SyntaxError is raised.
@ -205,7 +210,7 @@ class BaseEvaluator:
self.handle_error(exc, expression_source)
raise exc
try:
_locals = self._context
_locals = {sanitize_arg(x): y for x, y in self._context.items()}
# Yes this is an exec, yes it is potentially bad. Since we limit what variables are
# available here, and these policies can only be edited by admins, this is a risk
# we're willing to take.

View File

@ -4,13 +4,13 @@ from django.apps.registry import Apps
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from django.db import migrations
from django.contrib.auth.management import create_permissions
def migrate_search_group(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
from guardian.shortcuts import assign_perm
from authentik.core.models import User
from django.apps import apps as real_apps
from django.contrib.auth.management import create_permissions
from guardian.shortcuts import UserObjectPermission
db_alias = schema_editor.connection.alias
@ -20,16 +20,25 @@ def migrate_search_group(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
create_permissions(real_apps.get_app_config("authentik_providers_ldap"), using=db_alias)
LDAPProvider = apps.get_model("authentik_providers_ldap", "ldapprovider")
Permission = apps.get_model("auth", "Permission")
UserObjectPermission = apps.get_model("guardian", "UserObjectPermission")
ContentType = apps.get_model("contenttypes", "ContentType")
new_prem = Permission.objects.using(db_alias).get(codename="search_full_directory")
ct = ContentType.objects.using(db_alias).get(
app_label="authentik_providers_ldap",
model="ldapprovider",
)
for provider in LDAPProvider.objects.using(db_alias).all():
if not provider.search_group:
continue
for user_pk in (
provider.search_group.users.using(db_alias).all().values_list("pk", flat=True)
):
# We need the correct user model instance to assign the permission
assign_perm(
"search_full_directory", User.objects.using(db_alias).get(pk=user_pk), provider
for user in provider.search_group.users.using(db_alias).all():
UserObjectPermission.objects.using(db_alias).create(
user=user,
permission=new_prem,
object_pk=provider.pk,
content_type=ct,
)
@ -37,6 +46,7 @@ class Migration(migrations.Migration):
dependencies = [
("authentik_providers_ldap", "0003_ldapprovider_mfa_support_and_more"),
("guardian", "0002_generic_permissions_index"),
]
operations = [

View File

@ -248,7 +248,6 @@ MIDDLEWARE = [
"django.contrib.auth.middleware.AuthenticationMiddleware",
"authentik.core.middleware.RequestIDMiddleware",
"authentik.brands.middleware.BrandMiddleware",
"authentik.brands.middleware.BrandCORSAPIMiddleware",
"authentik.events.middleware.AuditMiddleware",
"django.middleware.security.SecurityMiddleware",
"django.middleware.common.CommonMiddleware",

View File

@ -1,6 +1,7 @@
"""authentik storage backends"""
import os
from urllib.parse import parse_qsl, urlsplit
from django.conf import settings
from django.core.exceptions import SuspiciousOperation
@ -110,3 +111,34 @@ class S3Storage(BaseS3Storage):
if self.querystring_auth:
return url
return self._strip_signing_parameters(url)
def _strip_signing_parameters(self, url):
# Boto3 does not currently support generating URLs that are unsigned. Instead
# we take the signed URLs and strip any querystring params related to signing
# and expiration.
# Note that this may end up with URLs that are still invalid, especially if
# params are passed in that only work with signed URLs, e.g. response header
# params.
# The code attempts to strip all query parameters that match names of known
# parameters from v2 and v4 signatures, regardless of the actual signature
# version used.
split_url = urlsplit(url)
qs = parse_qsl(split_url.query, keep_blank_values=True)
blacklist = {
"x-amz-algorithm",
"x-amz-credential",
"x-amz-date",
"x-amz-expires",
"x-amz-signedheaders",
"x-amz-signature",
"x-amz-security-token",
"awsaccesskeyid",
"expires",
"signature",
}
filtered_qs = ((key, val) for key, val in qs if key.lower() not in blacklist)
# Note: Parameters that did not have a value in the original query string will
# have an '=' sign appended to it, e.g ?foo&bar becomes ?foo=&bar=
joined_qs = ("=".join(keyval) for keyval in filtered_qs)
split_url = split_url._replace(query="&".join(joined_qs))
return split_url.geturl()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://goauthentik.io/blueprints/schema.json",
"type": "object",
"title": "authentik 2024.8.0 Blueprint schema",
"title": "authentik 2024.8.1 Blueprint schema",
"required": [
"version",
"entries"

View File

@ -31,7 +31,7 @@ services:
volumes:
- redis:/data
server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.8.0}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.8.1}
restart: unless-stopped
command: server
environment:
@ -52,7 +52,7 @@ services:
- postgresql
- redis
worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.8.0}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.8.1}
restart: unless-stopped
command: worker
environment:

2
go.mod
View File

@ -18,7 +18,7 @@ require (
github.com/gorilla/securecookie v1.1.2
github.com/gorilla/sessions v1.4.0
github.com/gorilla/websocket v1.5.3
github.com/jellydator/ttlcache/v3 v3.3.0
github.com/jellydator/ttlcache/v3 v3.2.1
github.com/mitchellh/mapstructure v1.5.0
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
github.com/pires/go-proxyproto v0.7.0

4
go.sum
View File

@ -200,8 +200,8 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jellydator/ttlcache/v3 v3.3.0 h1:BdoC9cE81qXfrxeb9eoJi9dWrdhSuwXMAnHTbnBm4Wc=
github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw=
github.com/jellydator/ttlcache/v3 v3.2.1 h1:eS8ljnYY7BllYGkXw/TfczWZrXUu/CH7SIkC6ugn9Js=
github.com/jellydator/ttlcache/v3 v3.2.1/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=

View File

@ -29,4 +29,4 @@ func UserAgent() string {
return fmt.Sprintf("authentik@%s", FullVersion())
}
const VERSION = "2024.8.0"
const VERSION = "2024.8.1"

View File

@ -35,10 +35,11 @@ func Paginator[Tobj any, Treq any, Tres PaginatorResponse[Tobj]](
req PaginatorRequest[Treq, Tres],
opts PaginatorOptions,
) ([]Tobj, error) {
var bfreq, cfreq interface{}
fetchOffset := func(page int32) (Tres, error) {
req.Page(page)
req.PageSize(int32(opts.PageSize))
res, _, err := req.Execute()
bfreq = req.Page(page)
cfreq = bfreq.(PaginatorRequest[Treq, Tres]).PageSize(int32(opts.PageSize))
res, _, err := cfreq.(PaginatorRequest[Treq, Tres]).Execute()
if err != nil {
opts.Logger.WithError(err).WithField("page", page).Warning("failed to fetch page")
}

View File

@ -0,0 +1,26 @@
package ak
// func Test_PaginatorCompile(t *testing.T) {
// req := api.ApiCoreUsersListRequest{}
// Paginator(req, PaginatorOptions{
// PageSize: 100,
// })
// }
// func Test_PaginatorCompileExplicit(t *testing.T) {
// req := api.ApiCoreUsersListRequest{}
// Paginator[
// api.User,
// api.ApiCoreUsersListRequest,
// *api.PaginatedUserList,
// ](req, PaginatorOptions{
// PageSize: 100,
// })
// }
// func Test_PaginatorCompileOther(t *testing.T) {
// req := api.ApiOutpostsProxyListRequest{}
// Paginator(req, PaginatorOptions{
// PageSize: 100,
// })
// }

View File

@ -96,7 +96,7 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
return ldap.LDAPResultOperationsError, nil
}
flags.UserPk = userInfo.User.Pk
flags.CanSearch = access.HasSearchPermission != nil
flags.CanSearch = access.GetHasSearchPermission()
db.si.SetFlags(req.BindDN, &flags)
if flags.CanSearch {
req.Log().Debug("Allowed access to search")

View File

@ -16,7 +16,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-18 00:08+0000\n"
"POT-Creation-Date: 2024-08-15 00:09+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Anton Babenko, 2024\n"
"Language-Team: Russian (https://app.transifex.com/authentik/teams/119923/ru/)\n"
@ -739,7 +739,7 @@ msgstr "Правило Уведомления"
#: authentik/events/models.py
msgid "Notification Rules"
msgstr "Правила уведомлений"
msgstr "Правило Уведомлений"
#: authentik/events/models.py
msgid "Webhook Mapping"
@ -1771,14 +1771,6 @@ msgstr "Сопоставление свойства Radius провайдера"
msgid "Radius Provider Property Mappings"
msgstr "Сопоставление свойств Radius провайдера"
#: authentik/providers/saml/api/providers.py
msgid ""
"With a signing keypair selected, at least one of 'Sign assertion' and 'Sign "
"Response' must be selected."
msgstr ""
"При выборе пары ключей для подписи необходимо выбрать как минимум один из "
"вариантов: 'Подписывать утверждение' или 'Подписывать ответ'."
#: authentik/providers/saml/api/providers.py
msgid "Invalid XML Syntax"
msgstr "Некорректный синтаксис XML"
@ -1926,21 +1918,6 @@ msgstr ""
msgid "Signing Keypair"
msgstr "Пара ключей для подписи"
#: authentik/providers/saml/models.py authentik/sources/saml/models.py
msgid ""
"When selected, incoming assertions are encrypted by the IdP using the public"
" key of the encryption keypair. The assertion is decrypted by the SP using "
"the the private key."
msgstr ""
"При выборе этого варианта, входящие утверждения шифруются поставщиком "
"идентификации (IdP) с использованием открытого ключа из пары ключей "
"шифрования. Утверждение расшифровывается поставщиком услуг (SP) с "
"использованием закрытого ключа."
#: authentik/providers/saml/models.py authentik/sources/saml/models.py
msgid "Encryption Keypair"
msgstr "Пара ключей шифрования"
#: authentik/providers/saml/models.py
msgid "Default relay_state value for IDP-initiated logins"
msgstr "Значение relay_state по умолчанию для логинов, инициированных IDP"
@ -2469,6 +2446,21 @@ msgstr ""
"Пара ключей, используемая для подписи исходящих ответов, направляемых "
"провайдеру идентификационных данных."
#: authentik/sources/saml/models.py
msgid ""
"When selected, incoming assertions are encrypted by the IdP using the public"
" key of the encryption keypair. The assertion is decrypted by the SP using "
"the the private key."
msgstr ""
"При выборе этого варианта, входящие утверждения шифруются поставщиком "
"идентификации (IdP) с использованием открытого ключа из пары ключей "
"шифрования. Утверждение расшифровывается поставщиком услуг (SP) с "
"использованием закрытого ключа."
#: authentik/sources/saml/models.py
msgid "Encryption Keypair"
msgstr "Пара ключей шифрования"
#: authentik/sources/saml/models.py
msgid "SAML Source"
msgstr "Источник SAML"

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,5 @@
{
"name": "@goauthentik/authentik",
"version": "2024.8.0",
"version": "2024.8.1",
"private": true
}

291
poetry.lock generated
View File

@ -1053,38 +1053,38 @@ toml = ["tomli"]
[[package]]
name = "cryptography"
version = "43.0.0"
version = "43.0.1"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
optional = false
python-versions = ">=3.7"
files = [
{file = "cryptography-43.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:64c3f16e2a4fc51c0d06af28441881f98c5d91009b8caaff40cf3548089e9c74"},
{file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3dcdedae5c7710b9f97ac6bba7e1052b95c7083c9d0e9df96e02a1932e777895"},
{file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d9a1eca329405219b605fac09ecfc09ac09e595d6def650a437523fcd08dd22"},
{file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ea9e57f8ea880eeea38ab5abf9fbe39f923544d7884228ec67d666abd60f5a47"},
{file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9a8d6802e0825767476f62aafed40532bd435e8a5f7d23bd8b4f5fd04cc80ecf"},
{file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cc70b4b581f28d0a254d006f26949245e3657d40d8857066c2ae22a61222ef55"},
{file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4a997df8c1c2aae1e1e5ac49c2e4f610ad037fc5a3aadc7b64e39dea42249431"},
{file = "cryptography-43.0.0-cp37-abi3-win32.whl", hash = "sha256:6e2b11c55d260d03a8cf29ac9b5e0608d35f08077d8c087be96287f43af3ccdc"},
{file = "cryptography-43.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:31e44a986ceccec3d0498e16f3d27b2ee5fdf69ce2ab89b52eaad1d2f33d8778"},
{file = "cryptography-43.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:7b3f5fe74a5ca32d4d0f302ffe6680fcc5c28f8ef0dc0ae8f40c0f3a1b4fca66"},
{file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac1955ce000cb29ab40def14fd1bbfa7af2017cca696ee696925615cafd0dce5"},
{file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:299d3da8e00b7e2b54bb02ef58d73cd5f55fb31f33ebbf33bd00d9aa6807df7e"},
{file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ee0c405832ade84d4de74b9029bedb7b31200600fa524d218fc29bfa371e97f5"},
{file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb013933d4c127349b3948aa8aaf2f12c0353ad0eccd715ca789c8a0f671646f"},
{file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fdcb265de28585de5b859ae13e3846a8e805268a823a12a4da2597f1f5afc9f0"},
{file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2905ccf93a8a2a416f3ec01b1a7911c3fe4073ef35640e7ee5296754e30b762b"},
{file = "cryptography-43.0.0-cp39-abi3-win32.whl", hash = "sha256:47ca71115e545954e6c1d207dd13461ab81f4eccfcb1345eac874828b5e3eaaf"},
{file = "cryptography-43.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:0663585d02f76929792470451a5ba64424acc3cd5227b03921dab0e2f27b1709"},
{file = "cryptography-43.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c6d112bf61c5ef44042c253e4859b3cbbb50df2f78fa8fae6747a7814484a70"},
{file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:844b6d608374e7d08f4f6e6f9f7b951f9256db41421917dfb2d003dde4cd6b66"},
{file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:51956cf8730665e2bdf8ddb8da0056f699c1a5715648c1b0144670c1ba00b48f"},
{file = "cryptography-43.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:aae4d918f6b180a8ab8bf6511a419473d107df4dbb4225c7b48c5c9602c38c7f"},
{file = "cryptography-43.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:232ce02943a579095a339ac4b390fbbe97f5b5d5d107f8a08260ea2768be8cc2"},
{file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5bcb8a5620008a8034d39bce21dc3e23735dfdb6a33a06974739bfa04f853947"},
{file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:08a24a7070b2b6804c1940ff0f910ff728932a9d0e80e7814234269f9d46d069"},
{file = "cryptography-43.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e9c5266c432a1e23738d178e51c2c7a5e2ddf790f248be939448c0ba2021f9d1"},
{file = "cryptography-43.0.0.tar.gz", hash = "sha256:b88075ada2d51aa9f18283532c9f60e72170041bba88d7f37e49cbb10275299e"},
{file = "cryptography-43.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d"},
{file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062"},
{file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962"},
{file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277"},
{file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a"},
{file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042"},
{file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494"},
{file = "cryptography-43.0.1-cp37-abi3-win32.whl", hash = "sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2"},
{file = "cryptography-43.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d"},
{file = "cryptography-43.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d"},
{file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806"},
{file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85"},
{file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c"},
{file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1"},
{file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa"},
{file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4"},
{file = "cryptography-43.0.1-cp39-abi3-win32.whl", hash = "sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47"},
{file = "cryptography-43.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb"},
{file = "cryptography-43.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034"},
{file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d"},
{file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289"},
{file = "cryptography-43.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84"},
{file = "cryptography-43.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365"},
{file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96"},
{file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172"},
{file = "cryptography-43.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2"},
{file = "cryptography-43.0.1.tar.gz", hash = "sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d"},
]
[package.dependencies]
@ -1097,7 +1097,7 @@ nox = ["nox"]
pep8test = ["check-sdist", "click", "mypy", "ruff"]
sdist = ["build"]
ssh = ["bcrypt (>=3.1.5)"]
test = ["certifi", "cryptography-vectors (==43.0.0)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
test = ["certifi", "cryptography-vectors (==43.0.1)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
test-randomorder = ["pytest-randomly"]
[[package]]
@ -1165,18 +1165,15 @@ files = [
[[package]]
name = "deepmerge"
version = "2.0"
description = "A toolset for deeply merging Python dictionaries."
version = "1.1.1"
description = "a toolset to deeply merge python dictionaries."
optional = false
python-versions = ">=3.8"
python-versions = "*"
files = [
{file = "deepmerge-2.0-py3-none-any.whl", hash = "sha256:6de9ce507115cff0bed95ff0ce9ecc31088ef50cbdf09bc90a09349a318b3d00"},
{file = "deepmerge-2.0.tar.gz", hash = "sha256:5c3d86081fbebd04dd5de03626a0607b809a98fb6ccba5770b62466fe940ff20"},
{file = "deepmerge-1.1.1-py3-none-any.whl", hash = "sha256:7219dad9763f15be9dcd4bcb53e00f48e4eed6f5ed8f15824223eb934bb35977"},
{file = "deepmerge-1.1.1.tar.gz", hash = "sha256:53a489dc9449636e480a784359ae2aab3191748c920649551c8e378622f0eca4"},
]
[package.extras]
dev = ["black", "build", "mypy", "pytest", "pyupgrade", "twine", "validate-pyproject[all]"]
[[package]]
name = "defusedxml"
version = "0.7.1"
@ -1761,13 +1758,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
[[package]]
name = "google-api-python-client"
version = "2.143.0"
version = "2.142.0"
description = "Google API Client Library for Python"
optional = false
python-versions = ">=3.7"
files = [
{file = "google_api_python_client-2.143.0-py2.py3-none-any.whl", hash = "sha256:d5654134522b9b574b82234e96f7e0aeeabcbf33643fbabcd449ef0068e3a476"},
{file = "google_api_python_client-2.143.0.tar.gz", hash = "sha256:6a75441f9078e6e2fcdf4946a153fda1e2cc81b5e9c8d6e8c0750c85c7f8a566"},
{file = "google_api_python_client-2.142.0-py2.py3-none-any.whl", hash = "sha256:266799082bb8301f423ec204dffbffb470b502abbf29efd1f83e644d36eb5a8f"},
{file = "google_api_python_client-2.142.0.tar.gz", hash = "sha256:a1101ac9e24356557ca22f07ff48b7f61fa5d4b4e7feeef3bda16e5dcb86350e"},
]
[package.dependencies]
@ -2050,13 +2047,13 @@ files = [
[[package]]
name = "importlib-metadata"
version = "8.4.0"
version = "8.0.0"
description = "Read metadata from Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"},
{file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"},
{file = "importlib_metadata-8.0.0-py3-none-any.whl", hash = "sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f"},
{file = "importlib_metadata-8.0.0.tar.gz", hash = "sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812"},
]
[package.dependencies]
@ -3028,49 +3025,49 @@ resolved_reference = "20d69d9cc50a0fef31605b46f06da0c94f1ec3cf"
[[package]]
name = "opentelemetry-api"
version = "1.27.0"
version = "1.26.0"
description = "OpenTelemetry Python API"
optional = false
python-versions = ">=3.8"
files = [
{file = "opentelemetry_api-1.27.0-py3-none-any.whl", hash = "sha256:953d5871815e7c30c81b56d910c707588000fff7a3ca1c73e6531911d53065e7"},
{file = "opentelemetry_api-1.27.0.tar.gz", hash = "sha256:ed673583eaa5f81b5ce5e86ef7cdaf622f88ef65f0b9aab40b843dcae5bef342"},
{file = "opentelemetry_api-1.26.0-py3-none-any.whl", hash = "sha256:7d7ea33adf2ceda2dd680b18b1677e4152000b37ca76e679da71ff103b943064"},
{file = "opentelemetry_api-1.26.0.tar.gz", hash = "sha256:2bd639e4bed5b18486fef0b5a520aaffde5a18fc225e808a1ac4df363f43a1ce"},
]
[package.dependencies]
deprecated = ">=1.2.6"
importlib-metadata = ">=6.0,<=8.4.0"
importlib-metadata = ">=6.0,<=8.0.0"
[[package]]
name = "opentelemetry-sdk"
version = "1.27.0"
version = "1.26.0"
description = "OpenTelemetry Python SDK"
optional = false
python-versions = ">=3.8"
files = [
{file = "opentelemetry_sdk-1.27.0-py3-none-any.whl", hash = "sha256:365f5e32f920faf0fd9e14fdfd92c086e317eaa5f860edba9cdc17a380d9197d"},
{file = "opentelemetry_sdk-1.27.0.tar.gz", hash = "sha256:d525017dea0ccce9ba4e0245100ec46ecdc043f2d7b8315d56b19aff0904fa6f"},
{file = "opentelemetry_sdk-1.26.0-py3-none-any.whl", hash = "sha256:feb5056a84a88670c041ea0ded9921fca559efec03905dddeb3885525e0af897"},
{file = "opentelemetry_sdk-1.26.0.tar.gz", hash = "sha256:c90d2868f8805619535c05562d699e2f4fb1f00dbd55a86dcefca4da6fa02f85"},
]
[package.dependencies]
opentelemetry-api = "1.27.0"
opentelemetry-semantic-conventions = "0.48b0"
opentelemetry-api = "1.26.0"
opentelemetry-semantic-conventions = "0.47b0"
typing-extensions = ">=3.7.4"
[[package]]
name = "opentelemetry-semantic-conventions"
version = "0.48b0"
version = "0.47b0"
description = "OpenTelemetry Semantic Conventions"
optional = false
python-versions = ">=3.8"
files = [
{file = "opentelemetry_semantic_conventions-0.48b0-py3-none-any.whl", hash = "sha256:a0de9f45c413a8669788a38569c7e0a11ce6ce97861a628cca785deecdc32a1f"},
{file = "opentelemetry_semantic_conventions-0.48b0.tar.gz", hash = "sha256:12d74983783b6878162208be57c9effcb89dc88691c64992d70bb89dc00daa1a"},
{file = "opentelemetry_semantic_conventions-0.47b0-py3-none-any.whl", hash = "sha256:4ff9d595b85a59c1c1413f02bba320ce7ea6bf9e2ead2b0913c4395c7bbc1063"},
{file = "opentelemetry_semantic_conventions-0.47b0.tar.gz", hash = "sha256:a8d57999bbe3495ffd4d510de26a97dadc1dace53e0275001b2c1b2f67992a7e"},
]
[package.dependencies]
deprecated = ">=1.2.6"
opentelemetry-api = "1.27.0"
opentelemetry-api = "1.26.0"
[[package]]
name = "orjson"
@ -3204,13 +3201,13 @@ files = [
[[package]]
name = "pdoc"
version = "14.6.1"
version = "14.6.0"
description = "API Documentation for Python Projects"
optional = false
python-versions = ">=3.8"
files = [
{file = "pdoc-14.6.1-py3-none-any.whl", hash = "sha256:efbed433655264392c60551615a3d42b8f21e492373419756d20234c667b54bc"},
{file = "pdoc-14.6.1.tar.gz", hash = "sha256:ee598f30d5c55dd4702086dabc412a26022acc35aa88aa382cda8ac655fead98"},
{file = "pdoc-14.6.0-py3-none-any.whl", hash = "sha256:36c42c546a317d8e3e8c0b39645f24161374de0c7066ccaae76628d721e49ba5"},
{file = "pdoc-14.6.0.tar.gz", hash = "sha256:6e98a24c5e0ca5d188397969cf82581836eaef13f172fc3820047bfe15c61c9a"},
]
[package.dependencies]
@ -3784,13 +3781,13 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments
[[package]]
name = "pytest-django"
version = "4.9.0"
version = "4.8.0"
description = "A Django plugin for pytest."
optional = false
python-versions = ">=3.8"
files = [
{file = "pytest_django-4.9.0-py3-none-any.whl", hash = "sha256:1d83692cb39188682dbb419ff0393867e9904094a549a7d38a3154d5731b2b99"},
{file = "pytest_django-4.9.0.tar.gz", hash = "sha256:8bf7bc358c9ae6f6fc51b6cebb190fe20212196e6807121f11bd6a3b03428314"},
{file = "pytest-django-4.8.0.tar.gz", hash = "sha256:5d054fe011c56f3b10f978f41a8efb2e5adfc7e680ef36fb571ada1f24779d90"},
{file = "pytest_django-4.8.0-py3-none-any.whl", hash = "sha256:ca1ddd1e0e4c227cf9e3e40a6afc6d106b3e70868fd2ac5798a22501271cd0c7"},
]
[package.dependencies]
@ -4198,29 +4195,29 @@ pyasn1 = ">=0.1.3"
[[package]]
name = "ruff"
version = "0.6.3"
version = "0.6.2"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
{file = "ruff-0.6.3-py3-none-linux_armv6l.whl", hash = "sha256:97f58fda4e309382ad30ede7f30e2791d70dd29ea17f41970119f55bdb7a45c3"},
{file = "ruff-0.6.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3b061e49b5cf3a297b4d1c27ac5587954ccb4ff601160d3d6b2f70b1622194dc"},
{file = "ruff-0.6.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:34e2824a13bb8c668c71c1760a6ac7d795ccbd8d38ff4a0d8471fdb15de910b1"},
{file = "ruff-0.6.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bddfbb8d63c460f4b4128b6a506e7052bad4d6f3ff607ebbb41b0aa19c2770d1"},
{file = "ruff-0.6.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ced3eeb44df75353e08ab3b6a9e113b5f3f996bea48d4f7c027bc528ba87b672"},
{file = "ruff-0.6.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47021dff5445d549be954eb275156dfd7c37222acc1e8014311badcb9b4ec8c1"},
{file = "ruff-0.6.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7d7bd20dc07cebd68cc8bc7b3f5ada6d637f42d947c85264f94b0d1cd9d87384"},
{file = "ruff-0.6.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:500f166d03fc6d0e61c8e40a3ff853fa8a43d938f5d14c183c612df1b0d6c58a"},
{file = "ruff-0.6.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42844ff678f9b976366b262fa2d1d1a3fe76f6e145bd92c84e27d172e3c34500"},
{file = "ruff-0.6.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70452a10eb2d66549de8e75f89ae82462159855e983ddff91bc0bce6511d0470"},
{file = "ruff-0.6.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:65a533235ed55f767d1fc62193a21cbf9e3329cf26d427b800fdeacfb77d296f"},
{file = "ruff-0.6.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2e2c23cef30dc3cbe9cc5d04f2899e7f5e478c40d2e0a633513ad081f7361b5"},
{file = "ruff-0.6.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d8a136aa7d228975a6aee3dd8bea9b28e2b43e9444aa678fb62aeb1956ff2351"},
{file = "ruff-0.6.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f92fe93bc72e262b7b3f2bba9879897e2d58a989b4714ba6a5a7273e842ad2f8"},
{file = "ruff-0.6.3-py3-none-win32.whl", hash = "sha256:7a62d3b5b0d7f9143d94893f8ba43aa5a5c51a0ffc4a401aa97a81ed76930521"},
{file = "ruff-0.6.3-py3-none-win_amd64.whl", hash = "sha256:746af39356fee2b89aada06c7376e1aa274a23493d7016059c3a72e3b296befb"},
{file = "ruff-0.6.3-py3-none-win_arm64.whl", hash = "sha256:14a9528a8b70ccc7a847637c29e56fd1f9183a9db743bbc5b8e0c4ad60592a82"},
{file = "ruff-0.6.3.tar.gz", hash = "sha256:183b99e9edd1ef63be34a3b51fee0a9f4ab95add123dbf89a71f7b1f0c991983"},
{file = "ruff-0.6.2-py3-none-linux_armv6l.whl", hash = "sha256:5c8cbc6252deb3ea840ad6a20b0f8583caab0c5ef4f9cca21adc5a92b8f79f3c"},
{file = "ruff-0.6.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:17002fe241e76544448a8e1e6118abecbe8cd10cf68fde635dad480dba594570"},
{file = "ruff-0.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3dbeac76ed13456f8158b8f4fe087bf87882e645c8e8b606dd17b0b66c2c1158"},
{file = "ruff-0.6.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:094600ee88cda325988d3f54e3588c46de5c18dae09d683ace278b11f9d4d534"},
{file = "ruff-0.6.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:316d418fe258c036ba05fbf7dfc1f7d3d4096db63431546163b472285668132b"},
{file = "ruff-0.6.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d72b8b3abf8a2d51b7b9944a41307d2f442558ccb3859bbd87e6ae9be1694a5d"},
{file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2aed7e243be68487aa8982e91c6e260982d00da3f38955873aecd5a9204b1d66"},
{file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d371f7fc9cec83497fe7cf5eaf5b76e22a8efce463de5f775a1826197feb9df8"},
{file = "ruff-0.6.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f310d63af08f583363dfb844ba8f9417b558199c58a5999215082036d795a1"},
{file = "ruff-0.6.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7db6880c53c56addb8638fe444818183385ec85eeada1d48fc5abe045301b2f1"},
{file = "ruff-0.6.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1175d39faadd9a50718f478d23bfc1d4da5743f1ab56af81a2b6caf0a2394f23"},
{file = "ruff-0.6.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b939f9c86d51635fe486585389f54582f0d65b8238e08c327c1534844b3bb9a"},
{file = "ruff-0.6.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d0d62ca91219f906caf9b187dea50d17353f15ec9bb15aae4a606cd697b49b4c"},
{file = "ruff-0.6.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7438a7288f9d67ed3c8ce4d059e67f7ed65e9fe3aa2ab6f5b4b3610e57e3cb56"},
{file = "ruff-0.6.2-py3-none-win32.whl", hash = "sha256:279d5f7d86696df5f9549b56b9b6a7f6c72961b619022b5b7999b15db392a4da"},
{file = "ruff-0.6.2-py3-none-win_amd64.whl", hash = "sha256:d9f3469c7dd43cd22eb1c3fc16926fb8258d50cb1b216658a07be95dd117b0f2"},
{file = "ruff-0.6.2-py3-none-win_arm64.whl", hash = "sha256:f28fcd2cd0e02bdf739297516d5643a945cc7caf09bd9bcb4d932540a5ea4fa9"},
{file = "ruff-0.6.2.tar.gz", hash = "sha256:239ee6beb9e91feb8e0ec384204a763f36cb53fb895a1a364618c6abb076b3be"},
]
[[package]]
@ -4259,13 +4256,13 @@ django-query = ["django (>=3.2)"]
[[package]]
name = "selenium"
version = "4.24.0"
version = "4.23.1"
description = "Official Python bindings for Selenium WebDriver"
optional = false
python-versions = ">=3.8"
files = [
{file = "selenium-4.24.0-py3-none-any.whl", hash = "sha256:42c23f60753d5415b261b236cecbd69bd4eb5271e1563915f546b443cb6b71c6"},
{file = "selenium-4.24.0.tar.gz", hash = "sha256:88281e5b5b90fe231868905d5ea745b9ee5e30db280b33498cc73fb0fa06d571"},
{file = "selenium-4.23.1-py3-none-any.whl", hash = "sha256:3a8d9f23dc636bd3840dd56f00c2739e32ec0c1e34a821dd553e15babef24477"},
{file = "selenium-4.23.1.tar.gz", hash = "sha256:128d099e66284437e7128d2279176ec7a06e6ec7426e167f5d34987166bd8f46"},
]
[package.dependencies]
@ -4655,13 +4652,13 @@ wsproto = ">=0.14"
[[package]]
name = "twilio"
version = "9.2.4"
version = "9.2.3"
description = "Twilio API client and TwiML generator"
optional = false
python-versions = ">=3.7.0"
files = [
{file = "twilio-9.2.4-py2.py3-none-any.whl", hash = "sha256:490da2518c0da370d738d436f9086b2463902707a811cd306ec8dcc8ce831758"},
{file = "twilio-9.2.4.tar.gz", hash = "sha256:454b7d075c6bee3b64c81c39151be1f9105c695df6dbb0021b0c43e2930263e7"},
{file = "twilio-9.2.3-py2.py3-none-any.whl", hash = "sha256:76bfc39aa8d854510907cb7f9465814dfdea9e91ec199bb44f0785f05746f4cc"},
{file = "twilio-9.2.3.tar.gz", hash = "sha256:da2255b5f3753cb3bf647fc6c50edbdb367ebc3cde6802806f6f863058a65f75"},
]
[package.dependencies]
@ -4672,13 +4669,13 @@ requests = ">=2.0.0"
[[package]]
name = "twisted"
version = "24.7.0"
version = "24.3.0"
description = "An asynchronous networking framework written in Python"
optional = false
python-versions = ">=3.8.0"
files = [
{file = "twisted-24.7.0-py3-none-any.whl", hash = "sha256:734832ef98108136e222b5230075b1079dad8a3fc5637319615619a7725b0c81"},
{file = "twisted-24.7.0.tar.gz", hash = "sha256:5a60147f044187a127ec7da96d170d49bcce50c6fd36f594e60f4587eff4d394"},
{file = "twisted-24.3.0-py3-none-any.whl", hash = "sha256:039f2e6a49ab5108abd94de187fa92377abe5985c7a72d68d0ad266ba19eae63"},
{file = "twisted-24.3.0.tar.gz", hash = "sha256:6b38b6ece7296b5e122c9eb17da2eeab3d98a198f50ca9efd00fb03e5b4fd4ae"},
]
[package.dependencies]
@ -4687,26 +4684,55 @@ automat = ">=0.8.0"
constantly = ">=15.1"
hyperlink = ">=17.1.1"
idna = {version = ">=2.4", optional = true, markers = "extra == \"tls\""}
incremental = ">=24.7.0"
incremental = ">=22.10.0"
pyopenssl = {version = ">=21.0.0", optional = true, markers = "extra == \"tls\""}
service-identity = {version = ">=18.1.0", optional = true, markers = "extra == \"tls\""}
twisted-iocpsupport = {version = ">=1.0.2,<2", markers = "platform_system == \"Windows\""}
typing-extensions = ">=4.2.0"
zope-interface = ">=5"
[package.extras]
all-non-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"]
all-non-platform = ["twisted[conch,http2,serial,test,tls]", "twisted[conch,http2,serial,test,tls]"]
conch = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)"]
dev = ["coverage (>=7.5,<8.0)", "cython-test-exception-raiser (>=1.0.2,<2)", "hypothesis (>=6.56)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "python-subunit (>=1.4,<2.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)"]
dev = ["coverage (>=6b1,<7)", "pyflakes (>=2.2,<3.0)", "python-subunit (>=1.4,<2.0)", "twisted[dev-release]", "twistedchecker (>=0.7,<1.0)"]
dev-release = ["pydoctor (>=23.9.0,<23.10.0)", "pydoctor (>=23.9.0,<23.10.0)", "sphinx (>=6,<7)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "towncrier (>=23.6,<24.0)"]
gtk-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pygobject", "pygobject", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"]
gtk-platform = ["pygobject", "pygobject", "twisted[all-non-platform]", "twisted[all-non-platform]"]
http2 = ["h2 (>=3.0,<5.0)", "priority (>=1.1.0,<2.0)"]
macos-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"]
mypy = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "coverage (>=7.5,<8.0)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "idna (>=2.4)", "mypy (>=1.8,<2.0)", "mypy-zope (>=1.0.3,<1.1.0)", "priority (>=1.1.0,<2.0)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "python-subunit (>=1.4,<2.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)", "types-pyopenssl", "types-setuptools"]
osx-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"]
macos-platform = ["pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "twisted[all-non-platform]", "twisted[all-non-platform]"]
mypy = ["mypy (>=1.8,<2.0)", "mypy-zope (>=1.0.3,<1.1.0)", "twisted[all-non-platform,dev]", "types-pyopenssl", "types-setuptools"]
osx-platform = ["twisted[macos-platform]", "twisted[macos-platform]"]
serial = ["pyserial (>=3.0)", "pywin32 (!=226)"]
test = ["cython-test-exception-raiser (>=1.0.2,<2)", "hypothesis (>=6.56)", "pyhamcrest (>=2)"]
tls = ["idna (>=2.4)", "pyopenssl (>=21.0.0)", "service-identity (>=18.1.0)"]
windows-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)", "twisted-iocpsupport (>=1.0.2)", "twisted-iocpsupport (>=1.0.2)"]
windows-platform = ["pywin32 (!=226)", "pywin32 (!=226)", "twisted[all-non-platform]", "twisted[all-non-platform]"]
[[package]]
name = "twisted-iocpsupport"
version = "1.0.4"
description = "An extension for use in the twisted I/O Completion Ports reactor."
optional = false
python-versions = "*"
files = [
{file = "twisted-iocpsupport-1.0.4.tar.gz", hash = "sha256:858096c0d15e33f15ac157f455d8f86f2f2cdd223963e58c0f682a3af8362d89"},
{file = "twisted_iocpsupport-1.0.4-cp310-cp310-win32.whl", hash = "sha256:afa2b630797f9ed2f27f3d9f55e3f72b4244911e45a8c82756f44babbf0b243e"},
{file = "twisted_iocpsupport-1.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:0058c963c8957bcd3deda62122e89953c9de1e867a274facc9b15dde1a9f31e8"},
{file = "twisted_iocpsupport-1.0.4-cp311-cp311-win32.whl", hash = "sha256:196f7c7ccad4ba4d1783b1c4e1d1b22d93c04275cd780bf7498d16c77319ad6e"},
{file = "twisted_iocpsupport-1.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:4e5f97bcbabdd79cbaa969b63439b89801ea560f11d42b0a387634275c633623"},
{file = "twisted_iocpsupport-1.0.4-cp312-cp312-win32.whl", hash = "sha256:6081bd7c2f4fcf9b383dcdb3b3385d75a26a7c9d2be25b6950c3d8ea652d2d2d"},
{file = "twisted_iocpsupport-1.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:76f7e67cec1f1d097d1f4ed7de41be3d74546e1a4ede0c7d56e775c4dce5dfb0"},
{file = "twisted_iocpsupport-1.0.4-cp36-cp36m-win32.whl", hash = "sha256:3d306fc4d88a6bcf61ce9d572c738b918578121bfd72891625fab314549024b5"},
{file = "twisted_iocpsupport-1.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:391ac4d6002a80e15f35adc4ad6056f4fe1c17ceb0d1f98ba01b0f4f917adfd7"},
{file = "twisted_iocpsupport-1.0.4-cp37-cp37m-win32.whl", hash = "sha256:0c1b5cf37f0b2d96cc3c9bc86fff16613b9f5d0ca565c96cf1f1fb8cfca4b81c"},
{file = "twisted_iocpsupport-1.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:3c5dc11d72519e55f727320e3cee535feedfaee09c0f0765ed1ca7badff1ab3c"},
{file = "twisted_iocpsupport-1.0.4-cp38-cp38-win32.whl", hash = "sha256:cc86c2ef598c15d824a243c2541c29459881c67fc3c0adb6efe2242f8f0ec3af"},
{file = "twisted_iocpsupport-1.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:c27985e949b9b1a1fb4c20c71d315c10ea0f93fdf3ccdd4a8c158b5926edd8c8"},
{file = "twisted_iocpsupport-1.0.4-cp39-cp39-win32.whl", hash = "sha256:e311dfcb470696e3c077249615893cada598e62fa7c4e4ca090167bd2b7d331f"},
{file = "twisted_iocpsupport-1.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:4574eef1f3bb81501fb02f911298af3c02fe8179c31a33b361dd49180c3e644d"},
{file = "twisted_iocpsupport-1.0.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:872747a3b64e2909aee59c803ccd0bceb9b75bf27915520ebd32d69687040fa2"},
{file = "twisted_iocpsupport-1.0.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:c2712b778bacf1db434e3e065adfed3db300754186a29aecac1efae9ef4bcaff"},
{file = "twisted_iocpsupport-1.0.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7c66fa0aa4236b27b3c61cb488662d85dae746a6d1c7b0d91cf7aae118445adf"},
{file = "twisted_iocpsupport-1.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:300437af17396a945a58dcfffd77863303a8b6d9e65c6e81f1d2eed55b50d444"},
]
[[package]]
name = "txaio"
@ -4870,41 +4896,46 @@ files = [
[[package]]
name = "watchdog"
version = "5.0.1"
version = "4.0.2"
description = "Filesystem events monitoring"
optional = false
python-versions = ">=3.9"
python-versions = ">=3.8"
files = [
{file = "watchdog-5.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a6b8c6c82ada78479a0df568d27d69aa07105aba9301ac66d1ae162645f4ba34"},
{file = "watchdog-5.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e8ca9b7f5f03d2f0556a43db1e9adf1e5af6adf52e0890f781324514b67a612"},
{file = "watchdog-5.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c92812a358eabebe92b12b9290d16dc95c8003654658f6b2676c9a2103a73ceb"},
{file = "watchdog-5.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a03a6ccb846ead406a25a0b702d0a6b88fdfa77becaf907cfcfce7737ebbda1f"},
{file = "watchdog-5.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:39f0de161a822402f0f00c68b82349a4d71c9814e749148ca2b083a25606dbf9"},
{file = "watchdog-5.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5541a8765c4090decb4dba55d3dceb57724748a717ceaba8dc4f213edb0026e0"},
{file = "watchdog-5.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e321f1561adea30e447130882efe451af519646178d04189d6ba91a8cd7d88a5"},
{file = "watchdog-5.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c4ae0b3e95455fa9d959aa3b253c87845ad454ef188a4bf5a69cab287c131216"},
{file = "watchdog-5.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b2d56425dfa0c1e6f8a510f21d3d54ef7fe50bbc29638943c2cb1394b7b49156"},
{file = "watchdog-5.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:70e30116849f4ec52240eb1fad83d27e525eae179bfe1c09b3bf120163d731b6"},
{file = "watchdog-5.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f66df2c152edf5a2fe472bb2f8a5d562165bcf6cf9686cee5d75e524c21ca895"},
{file = "watchdog-5.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6bb68d9adb9c45f0dc1c2b12f4fb6eab0463a8f9741e371e4ede6769064e0785"},
{file = "watchdog-5.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6fbb4dd5ace074a2969825fde10034b35b31efcb6973defb22eb945b1d3acc37"},
{file = "watchdog-5.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:753c6a4c1eea9d3b96cd58159b49103e66cb288216a414ab9ad234ccc7642ec2"},
{file = "watchdog-5.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:20a28c8b0b3edf4ea2b27fb3527fc0a348e983f22a4317d316bb561524391932"},
{file = "watchdog-5.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a1cd7c919940b15f253db8279a579fb81e4e4e434b39b11a1cb7f54fe3fa46a6"},
{file = "watchdog-5.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a791dfc050ed24b82f7f100ae794192594fe863a7e9bdafcdfa5c6e405a981e5"},
{file = "watchdog-5.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8ba1472b5fa7c644e49641f70d7ccc567f70b54d776defa5d6f755dc2edc3fbb"},
{file = "watchdog-5.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b21e6601efe8453514c2fc21aca57fb5413c3d8b157bfe520b05b57b1788a167"},
{file = "watchdog-5.0.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:763c6f82bb65504b47d4aea268462b2fb662676676356e04787f332a11f03eb0"},
{file = "watchdog-5.0.1-py3-none-manylinux2014_armv7l.whl", hash = "sha256:664917cd513538728875a42d5654584b533da88cf06680452c98e73b45466968"},
{file = "watchdog-5.0.1-py3-none-manylinux2014_i686.whl", hash = "sha256:39e828c4270452b966bc9d814911a3c7e24c62d726d2a3245f5841664ff56b5e"},
{file = "watchdog-5.0.1-py3-none-manylinux2014_ppc64.whl", hash = "sha256:59ec6111f3750772badae3403ef17263489ed6f27ac01ec50c0244b2afa258fb"},
{file = "watchdog-5.0.1-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f3006361dba2005552cc8aa49c44d16a10e0a1939bb3286e888a14f722122808"},
{file = "watchdog-5.0.1-py3-none-manylinux2014_s390x.whl", hash = "sha256:72dbdffe4aa0c36c59f4a5190bceeb7fdfdf849ab98a562b3a783a64cc6dacdd"},
{file = "watchdog-5.0.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c93aa24899cb4e8a51492c7ccc420bea45ced502fe9ef2e83f9ab1107e5a13b5"},
{file = "watchdog-5.0.1-py3-none-win32.whl", hash = "sha256:2b8cd627b76194e725ed6f48d9524b1ad93a51a0dc3bd0225c56023716245091"},
{file = "watchdog-5.0.1-py3-none-win_amd64.whl", hash = "sha256:4eaebff2f938f5325788cef26521891b2d8ecc8e7852aa123a9b458815f93875"},
{file = "watchdog-5.0.1-py3-none-win_ia64.whl", hash = "sha256:9b1b32f89f95162f09aea6e15d9384f6e0490152f10d7ed241f8a85cddc50658"},
{file = "watchdog-5.0.1.tar.gz", hash = "sha256:f0180e84e6493ef7c82e051334e8c9b00ffd89fa9de5e0613d3c267f6ccf2d38"},
{file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ede7f010f2239b97cc79e6cb3c249e72962404ae3865860855d5cbe708b0fd22"},
{file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2cffa171445b0efa0726c561eca9a27d00a1f2b83846dbd5a4f639c4f8ca8e1"},
{file = "watchdog-4.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c50f148b31b03fbadd6d0b5980e38b558046b127dc483e5e4505fcef250f9503"},
{file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c7d4bf585ad501c5f6c980e7be9c4f15604c7cc150e942d82083b31a7548930"},
{file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:914285126ad0b6eb2258bbbcb7b288d9dfd655ae88fa28945be05a7b475a800b"},
{file = "watchdog-4.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:984306dc4720da5498b16fc037b36ac443816125a3705dfde4fd90652d8028ef"},
{file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1cdcfd8142f604630deef34722d695fb455d04ab7cfe9963055df1fc69e6727a"},
{file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d7ab624ff2f663f98cd03c8b7eedc09375a911794dfea6bf2a359fcc266bff29"},
{file = "watchdog-4.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:132937547a716027bd5714383dfc40dc66c26769f1ce8a72a859d6a48f371f3a"},
{file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd67c7df93eb58f360c43802acc945fa8da70c675b6fa37a241e17ca698ca49b"},
{file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcfd02377be80ef3b6bc4ce481ef3959640458d6feaae0bd43dd90a43da90a7d"},
{file = "watchdog-4.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:980b71510f59c884d684b3663d46e7a14b457c9611c481e5cef08f4dd022eed7"},
{file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:aa160781cafff2719b663c8a506156e9289d111d80f3387cf3af49cedee1f040"},
{file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f6ee8dedd255087bc7fe82adf046f0b75479b989185fb0bdf9a98b612170eac7"},
{file = "watchdog-4.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0b4359067d30d5b864e09c8597b112fe0a0a59321a0f331498b013fb097406b4"},
{file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:770eef5372f146997638d737c9a3c597a3b41037cfbc5c41538fc27c09c3a3f9"},
{file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eeea812f38536a0aa859972d50c76e37f4456474b02bd93674d1947cf1e39578"},
{file = "watchdog-4.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b2c45f6e1e57ebb4687690c05bc3a2c1fb6ab260550c4290b8abb1335e0fd08b"},
{file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:10b6683df70d340ac3279eff0b2766813f00f35a1d37515d2c99959ada8f05fa"},
{file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7c739888c20f99824f7aa9d31ac8a97353e22d0c0e54703a547a218f6637eb3"},
{file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c100d09ac72a8a08ddbf0629ddfa0b8ee41740f9051429baa8e31bb903ad7508"},
{file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:f5315a8c8dd6dd9425b974515081fc0aadca1d1d61e078d2246509fd756141ee"},
{file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2d468028a77b42cc685ed694a7a550a8d1771bb05193ba7b24006b8241a571a1"},
{file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f15edcae3830ff20e55d1f4e743e92970c847bcddc8b7509bcd172aa04de506e"},
{file = "watchdog-4.0.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:936acba76d636f70db8f3c66e76aa6cb5136a936fc2a5088b9ce1c7a3508fc83"},
{file = "watchdog-4.0.2-py3-none-manylinux2014_armv7l.whl", hash = "sha256:e252f8ca942a870f38cf785aef420285431311652d871409a64e2a0a52a2174c"},
{file = "watchdog-4.0.2-py3-none-manylinux2014_i686.whl", hash = "sha256:0e83619a2d5d436a7e58a1aea957a3c1ccbf9782c43c0b4fed80580e5e4acd1a"},
{file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64.whl", hash = "sha256:88456d65f207b39f1981bf772e473799fcdc10801062c36fd5ad9f9d1d463a73"},
{file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:32be97f3b75693a93c683787a87a0dc8db98bb84701539954eef991fb35f5fbc"},
{file = "watchdog-4.0.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:c82253cfc9be68e3e49282831afad2c1f6593af80c0daf1287f6a92657986757"},
{file = "watchdog-4.0.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c0b14488bd336c5b1845cee83d3e631a1f8b4e9c5091ec539406e4a324f882d8"},
{file = "watchdog-4.0.2-py3-none-win32.whl", hash = "sha256:0d8a7e523ef03757a5aa29f591437d64d0d894635f8a50f370fe37f913ce4e19"},
{file = "watchdog-4.0.2-py3-none-win_amd64.whl", hash = "sha256:c344453ef3bf875a535b0488e3ad28e341adbd5a9ffb0f7d62cefacc8824ef2b"},
{file = "watchdog-4.0.2-py3-none-win_ia64.whl", hash = "sha256:baececaa8edff42cd16558a639a9b0ddf425f93d892e8392a56bf904f5eff22c"},
{file = "watchdog-4.0.2.tar.gz", hash = "sha256:b4dfbb6c49221be4535623ea4474a4d6ee0a9cef4a80b20c28db4d858b64e270"},
]
[package.extras]

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "authentik"
version = "2024.8.0"
version = "2024.8.1"
description = ""
authors = ["authentik Team <hello@goauthentik.io>"]

View File

@ -1,7 +1,7 @@
openapi: 3.0.3
info:
title: authentik
version: 2024.8.0
version: 2024.8.1
description: Making authentication simple.
contact:
email: hello@goauthentik.io

View File

@ -11,6 +11,7 @@ from ldap3.core.exceptions import LDAPInvalidCredentialsResult
from authentik.blueprints.tests import apply_blueprint, reconcile_app
from authentik.core.models import Application, User
from authentik.core.tests.utils import create_test_user
from authentik.events.models import Event, EventAction
from authentik.flows.models import Flow
from authentik.lib.generators import generate_id
@ -331,6 +332,83 @@ class TestProviderLDAP(SeleniumTestCase):
]
self.assert_list_dict_equal(expected, response)
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@reconcile_app("authentik_tenants")
@reconcile_app("authentik_outposts")
def test_ldap_bind_search_no_perms(self):
"""Test simple bind + search"""
user = create_test_user()
self._prepare()
server = Server("ldap://localhost:3389", get_info=ALL)
_connection = Connection(
server,
raise_exceptions=True,
user=f"cn={user.username},ou=users,dc=ldap,dc=goauthentik,dc=io",
password=user.username,
)
_connection.bind()
self.assertTrue(
Event.objects.filter(
action=EventAction.LOGIN,
user={
"pk": user.pk,
"email": user.email,
"username": user.username,
},
)
)
_connection.search(
"ou=Users,DC=ldaP,dc=goauthentik,dc=io",
"(objectClass=user)",
search_scope=SUBTREE,
attributes=[ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES],
)
response: list = _connection.response
# Remove raw_attributes to make checking easier
for obj in response:
del obj["raw_attributes"]
del obj["raw_dn"]
obj["attributes"] = dict(obj["attributes"])
expected = [
{
"dn": f"cn={user.username},ou=users,dc=ldap,dc=goauthentik,dc=io",
"attributes": {
"cn": user.username,
"sAMAccountName": user.username,
"uid": user.uid,
"name": user.name,
"displayName": user.name,
"sn": user.name,
"mail": user.email,
"objectClass": [
"top",
"person",
"organizationalPerson",
"inetOrgPerson",
"user",
"posixAccount",
"goauthentik.io/ldap/user",
],
"uidNumber": 2000 + user.pk,
"gidNumber": 2000 + user.pk,
"memberOf": [
f"cn={group.name},ou=groups,dc=ldap,dc=goauthentik,dc=io"
for group in user.ak_groups.all()
],
"homeDirectory": f"/home/{user.username}",
"ak-active": True,
"ak-superuser": False,
},
"type": "searchResEntry",
},
]
self.assert_list_dict_equal(expected, response)
def assert_list_dict_equal(self, expected: list[dict], actual: list[dict], match_key="dn"):
"""Assert a list of dictionaries is identical, ignoring the ordering of items"""
self.assertEqual(len(expected), len(actual))

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,9 @@
{
"name": "@goauthentik/web-tests",
"dependencies": {
"chromedriver": "^128.0.1",
"lockfile-lint": "^4.14.0",
"syncpack": "^13.0.0"
},
"private": true,
"type": "module",
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/mocha": "^10.0.7",
"@typescript-eslint/eslint-plugin": "^7.17.0",
"@typescript-eslint/parser": "^7.17.0",
"@wdio/cli": "^9.0.3",
@ -23,20 +19,19 @@
"typescript": "^5.5.4",
"wdio-wait-for": "^3.0.11"
},
"scripts": {
"wdio": "wdio run ./wdio.conf.ts",
"lint:precommit": "eslint --max-warnings 0 --config ./.eslintrc.precommit.json $(git status --porcelain . | grep '^[AM?][M?]' | cut -d'/' -f3- | grep -E '\\.(ts|js|tsx|jsx)$')",
"lint": "eslint . --max-warnings 0 --fix",
"lint:spelling": "codespell -D - -D $(git rev-parse --show-toplevel 2> /dev/null)/.github/codespell-dictionary.txt -I $(git rev-parse --show-toplevel 2> /dev/null)/.github/codespell-words.txt ./test -s",
"precommit": "run-s lint:precommit lint:spelling prettier",
"prettier-check": "prettier --check .",
"prettier": "prettier --write ."
},
"engines": {
"node": ">=20"
},
"private": true,
"scripts": {
"lint": "eslint . --max-warnings 0 --fix",
"lint:lockfile": "lockfile-lint --path package.json --type npm --allowed-hosts npm --validate-https",
"lint:package": "syncpack format -i ' '",
"lint:precommit": "eslint --max-warnings 0 --config ./.eslintrc.precommit.json $(git status --porcelain . | grep '^[AM?][M?]' | cut -d'/' -f3- | grep -E '\\.(ts|js|tsx|jsx)$')",
"lint:spelling": "codespell -D - -D $(git rev-parse --show-toplevel 2> /dev/null)/.github/codespell-dictionary.txt -I $(git rev-parse --show-toplevel 2> /dev/null)/.github/codespell-words.txt ./test -s",
"precommit": "run-s lint:precommit lint:spelling prettier",
"prettier": "prettier --write .",
"prettier-check": "prettier --check .",
"wdio": "wdio run ./wdio.conf.ts"
},
"type": "module"
"dependencies": {
"chromedriver": "^128.0.0"
}
}

View File

@ -1,11 +1,25 @@
import Page from "../pageobjects/page.js";
import { browser } from "@wdio/globals";
const CLICK_TIME_DELAY = 250;
export default class AdminPage extends Page {
public async pageHeader() {
return await $("ak-page-header").$('slot[name="header"]');
public get pageHeader() {
return $('>>>ak-page-header slot[name="header"]');
}
async openApplicationsListPage() {
await this.open("if/admin/#/core/applications");
}
public open(path: string) {
return browser.url(`http://localhost:9000/${path}`);
}
public pause(selector?: string) {
if (selector) {
return $(selector).waitForDisplayed();
}
return browser.pause(CLICK_TIME_DELAY);
}
}

View File

@ -27,24 +27,24 @@ class ApplicationWizardView extends AdminPage {
radius = RadiusForm;
app = ApplicationForm;
async wizardTitle() {
return await $("ak-wizard-frame").$(".pf-c-wizard__title");
get wizardTitle() {
return $(">>>ak-wizard-frame .pf-c-wizard__header h1.pf-c-title");
}
async providerList() {
return await $("ak-application-wizard-authentication-method-choice");
get providerList() {
return $(">>>ak-application-wizard-authentication-method-choice");
}
async nextButton() {
return await $("ak-wizard-frame").$("footer button.pf-m-primary");
get nextButton() {
return $(">>>ak-wizard-frame footer button.pf-m-primary");
}
async getProviderType(type: string) {
return await this.providerList().$(`input[value="${type}"]`);
return await this.providerList.$(`>>>input[value="${type}"]`);
}
async successMessage() {
return await $('[data-commit-state="success"]');
get successMessage() {
return $('>>>[data-commit-state="success"]');
}
}
@ -65,10 +65,8 @@ const providerValues: Pair[] = [
providerValues.forEach(([value, name]: Pair) => {
Object.defineProperties(ApplicationWizardView.prototype, {
[name]: {
get: async function () {
return await (
await this.providerList()
).$(`div[data-ouid-component-name="${value}"]`);
get: function () {
return this.providerList.$(`>>>input[value="${value}"]`);
},
},
});

View File

@ -9,8 +9,8 @@ class ApplicationsListPage extends AdminPage {
* define selectors using getter methods
*/
async startWizardButton() {
return await $("ak-application-wizard").$('button[slot="trigger"]');
get startWizardButton() {
return $('>>>ak-wizard-frame button[slot="trigger"]');
}
async open() {

View File

@ -2,16 +2,16 @@ import Page from "../page.js";
import { $ } from "@wdio/globals";
export class ApplicationForm extends Page {
async name() {
return await $('ak-text-input[name="name"]').$("input");
get name() {
return $('>>>ak-form-element-horizontal input[name="name"]');
}
async uiSettings() {
return await $("ak-form-group").$('button[aria-label="UI Settings"]');
get uiSettings() {
return $('>>>ak-form-group button[aria-label="UI Settings"]');
}
async launchUrl() {
return await $('input[name="metaLaunchUrl"]');
get launchUrl() {
return $('>>>input[name="metaLaunchUrl"]');
}
}

View File

@ -4,14 +4,14 @@ import { $ } from "@wdio/globals";
export class ForwardProxyForm extends Page {
async setAuthorizationFlow(selector: string) {
await this.searchSelect(
'ak-flow-search[name="authorizationFlow"]',
'>>>ak-flow-search[name="authorizationFlow"] input[type="text"]',
"authorizationFlow",
selector,
`button*=${selector}`,
);
}
get externalHost() {
return $('input[name="externalHost"]');
return $('>>>input[name="externalHost"]');
}
}

View File

@ -1,11 +1,11 @@
import Page from "../page.js";
export class LdapForm extends Page {
async setBindFlow(_selector: string) {
async setBindFlow(selector: string) {
await this.searchSelect(
'ak-search-select-view[name="authorizationFlow"]',
'>>>ak-branded-flow-search[name="authorizationFlow"] input[type="text"]',
"authorizationFlow",
"default-authentication-flow",
`button*=${selector}`,
);
}
}

View File

@ -4,14 +4,14 @@ import { $ } from "@wdio/globals";
export class OauthForm extends Page {
async setAuthorizationFlow(selector: string) {
await this.searchSelect(
'ak-flow-search[name="authorizationFlow"]',
'>>>ak-flow-search[name="authorizationFlow"] input[type="text"]',
"authorizationFlow",
`${selector}`,
`button*=${selector}`,
);
}
async providerName() {
return await $('ak-form-element-horizontal[name="name"]').$("input");
get providerName() {
return $('>>>ak-form-element-horizontal[name="name"] input');
}
}

View File

@ -3,9 +3,9 @@ import Page from "../page.js";
export class RadiusForm extends Page {
async setAuthenticationFlow(selector: string) {
await this.searchSelect(
'ak-branded-flow-search[name="authorizationFlow"]',
'>>>ak-branded-flow-search[name="authorizationFlow"] input[type="text"]',
"authorizationFlow",
selector,
`button*=${selector}`,
);
}
}

View File

@ -4,14 +4,14 @@ import { $ } from "@wdio/globals";
export class SamlForm extends Page {
async setAuthorizationFlow(selector: string) {
await this.searchSelect(
'ak-flow-search[name="authorizationFlow"]',
'>>>ak-flow-search[name="authorizationFlow"] input[type="text"]',
"authorizationFlow",
selector,
`button*=${selector}`,
);
}
get acsUrl() {
return $('input[name="acsUrl"]');
return $('>>>input[name="acsUrl"]');
}
}

View File

@ -2,11 +2,11 @@ import Page from "../page.js";
export class ScimForm extends Page {
get url() {
return $('input[name="url"]');
return $('>>>input[name="url"]');
}
get token() {
return $('input[name="token"]');
return $('>>>input[name="token"]');
}
}

View File

@ -4,18 +4,18 @@ import { $ } from "@wdio/globals";
export class TransparentProxyForm extends Page {
async setAuthorizationFlow(selector: string) {
await this.searchSelect(
'ak-flow-search[name="authorizationFlow"]',
'>>>ak-flow-search[name="authorizationFlow"] input[type="text"]',
"authorizationFlow",
selector,
`button*=${selector}`,
);
}
get externalHost() {
return $('input[name="externalHost"]');
return $('>>>input[name="externalHost"]');
}
get internalHost() {
return $('input[name="internalHost"]');
return $('>>>input[name="internalHost"]');
}
}

View File

@ -9,20 +9,20 @@ class LoginPage extends Page {
/**
* Selectors
*/
async inputUsername() {
return await $('input[name="uidField"]');
get inputUsername() {
return $('>>>input[name="uidField"]');
}
async inputPassword() {
return await $('input[name="password"]');
get inputPassword() {
return $('>>>input[name="password"]');
}
async btnSubmit() {
return await $('button[type="submit"]');
get btnSubmit() {
return $('>>>button[type="submit"]');
}
async authFailure() {
return await $(".pf-m-error");
get authFailure() {
return $(">>>h4.pf-c-alert__title");
}
/**
@ -30,15 +30,17 @@ class LoginPage extends Page {
*/
async username(username: string) {
await (await this.inputUsername()).setValue(username);
await (await this.btnSubmit()).waitForEnabled();
await (await this.btnSubmit()).click();
await this.inputUsername.waitForClickable();
await this.inputUsername.setValue(username);
await this.btnSubmit.waitForEnabled();
await this.btnSubmit.click();
}
async password(password: string) {
await (await this.inputPassword()).setValue(password);
await (await this.btnSubmit()).waitForEnabled();
await (await this.btnSubmit()).click();
await this.inputPassword.waitForClickable();
await this.inputPassword.setValue(password);
await this.btnSubmit.waitForEnabled();
await this.btnSubmit.click();
}
async login(username: string, password: string) {
@ -46,7 +48,7 @@ class LoginPage extends Page {
await this.pause();
await this.password(password);
await this.pause();
await this.pause("div.header h1");
await this.pause(">>>div.header h1");
return UserLibraryPage;
}

View File

@ -1,5 +1,4 @@
import { browser } from "@wdio/globals";
import { Key } from "webdriverio";
const CLICK_TIME_DELAY = 250;
@ -12,15 +11,15 @@ export default class Page {
* Opens a sub page of the page
* @param path path of the sub page (e.g. /path/to/page.html)
*/
public async open(path: string) {
return await browser.url(`http://localhost:9000/${path}`);
public open(path: string) {
return browser.url(`http://localhost:9000/${path}`);
}
public async pause(selector?: string) {
public pause(selector?: string) {
if (selector) {
return await $(selector).waitForDisplayed();
return $(selector).waitForDisplayed();
}
return await browser.pause(CLICK_TIME_DELAY);
return browser.pause(CLICK_TIME_DELAY);
}
/**
@ -34,20 +33,10 @@ export default class Page {
async searchSelect(searchSelector: string, managedSelector: string, buttonSelector: string) {
const inputBind = await $(searchSelector);
const inputMain = await inputBind.$('input[type="text"]');
await inputMain.click();
const searchBlock = await (
await $(`div[data-managed-for="${managedSelector}"]`).$("ak-list-select")
).shadow$$("button");
let target: WebdriverIO.Element;
for (const button of searchBlock) {
if ((await button.getText()).includes(buttonSelector)) {
target = button;
break;
}
}
await (await target).click();
await browser.keys(Key.Tab);
await inputBind.click();
const searchBlock = await $(`>>>div[data-managed-for="${managedSelector}"]`);
const target = searchBlock.$(buttonSelector);
return await target.click();
}
public async logout() {

View File

@ -9,13 +9,13 @@ class UserLibraryPage extends Page {
* define selectors using getter methods
*/
public async pageHeader() {
return await $('h1[aria-level="1"]');
public get pageHeader() {
return $('>>>h1[aria-level="1"]');
}
public async goToAdmin() {
await $('a[href="/if/admin"]').click();
return await $("ak-admin-overview").waitForDisplayed();
await $('>>>a[href="/if/admin"]').click();
await $(">>>ak-admin-overview").waitForDisplayed();
}
}

View File

@ -1,15 +0,0 @@
import LoginPage from "../pageobjects/login.page.js";
import { BAD_PASSWORD, GOOD_USERNAME } from "../utils/constants.js";
import { expect } from "@wdio/globals";
describe("Log into authentik", () => {
it("should fail on a bad password", async () => {
await LoginPage.open();
await LoginPage.username(GOOD_USERNAME);
await LoginPage.pause();
await LoginPage.password(BAD_PASSWORD);
const failure = await LoginPage.authFailure();
await expect(failure).toBeDisplayedInViewport();
await expect(failure).toHaveText("Invalid password");
});
});

View File

@ -1,15 +1,21 @@
import LoginPage from "../pageobjects/login.page.js";
import { BAD_USERNAME, GOOD_PASSWORD } from "../utils/constants.js";
import { BAD_PASSWORD, BAD_USERNAME, GOOD_USERNAME } from "../utils/constants.js";
import { expect } from "@wdio/globals";
describe("Log into authentik", () => {
it("should fail on a bad username", async () => {
await LoginPage.open();
await LoginPage.username(BAD_USERNAME);
const failure = await LoginPage.authFailure;
expect(failure).toHaveText("Failed to authenticate.");
});
it("should fail on a bad password", async () => {
await LoginPage.open();
await LoginPage.username(GOOD_USERNAME);
await LoginPage.pause();
await LoginPage.password(GOOD_PASSWORD);
const failure = await LoginPage.authFailure();
await expect(failure).toBeDisplayedInViewport();
await expect(failure).toHaveText("Invalid password");
await LoginPage.password(BAD_PASSWORD);
const failure = await LoginPage.authFailure;
expect(failure).toHaveText("Failed to authenticate.");
});
});

View File

@ -10,27 +10,25 @@ async function reachTheProvider(title: string) {
await ApplicationsListPage.logout();
await login();
await ApplicationsListPage.open();
await ApplicationsListPage.pause("ak-page-header");
await expect(await ApplicationsListPage.pageHeader()).toBeDisplayed();
await expect(await ApplicationsListPage.pageHeader()).toHaveText("Applications");
await expect(await ApplicationsListPage.pageHeader).toHaveText("Applications");
await (await ApplicationsListPage.startWizardButton()).click();
await (await ApplicationWizardView.wizardTitle()).waitForDisplayed();
await expect(await ApplicationWizardView.wizardTitle()).toHaveText("New application");
await ApplicationsListPage.startWizardButton.click();
await ApplicationWizardView.wizardTitle.waitForDisplayed();
await expect(await ApplicationWizardView.wizardTitle).toHaveText("New application");
await (await ApplicationWizardView.app.name()).setValue(`${title} - ${newPrefix}`);
await (await ApplicationWizardView.app.uiSettings()).scrollIntoView();
await (await ApplicationWizardView.app.uiSettings()).click();
await (await ApplicationWizardView.app.launchUrl()).scrollIntoView();
await (await ApplicationWizardView.app.launchUrl()).setValue("http://example.goauthentik.io");
await ApplicationWizardView.app.name.setValue(`${title} - ${newPrefix}`);
await ApplicationWizardView.app.uiSettings.scrollIntoView();
await ApplicationWizardView.app.uiSettings.click();
await ApplicationWizardView.app.launchUrl.scrollIntoView();
await ApplicationWizardView.app.launchUrl.setValue("http://example.goauthentik.io");
await (await ApplicationWizardView.nextButton()).click();
await ApplicationWizardView.nextButton.click();
return await ApplicationWizardView.pause();
}
async function getCommitMessage() {
await (await ApplicationWizardView.successMessage()).waitForDisplayed();
return await ApplicationWizardView.successMessage();
await ApplicationWizardView.successMessage.waitForDisplayed();
return await ApplicationWizardView.successMessage;
}
const SUCCESS_MESSAGE = "Your application has been saved";
@ -40,97 +38,97 @@ describe("Configure Applications with the Application Wizard", () => {
it("Should configure a simple LDAP Application", async () => {
await reachTheProvider("New LDAP Application");
await (await ApplicationWizardView.providerList()).waitForDisplayed();
await (await ApplicationWizardView.ldapProvider).scrollIntoView();
await (await ApplicationWizardView.ldapProvider).click();
await ApplicationWizardView.providerList.waitForDisplayed();
await ApplicationWizardView.ldapProvider.scrollIntoView();
await ApplicationWizardView.ldapProvider.click();
await (await ApplicationWizardView.nextButton()).click();
await ApplicationWizardView.nextButton.click();
await ApplicationWizardView.pause();
await ApplicationWizardView.ldap.setBindFlow("default-authentication-flow");
await (await ApplicationWizardView.nextButton()).click();
await ApplicationWizardView.nextButton.click();
await ApplicationWizardView.pause();
await expect(await getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
await expect(getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
});
it("Should configure a simple Oauth2 Application", async () => {
await reachTheProvider("New Oauth2 Application");
await (await ApplicationWizardView.providerList()).waitForDisplayed();
await (await ApplicationWizardView.oauth2Provider).scrollIntoView();
await (await ApplicationWizardView.oauth2Provider).click();
await ApplicationWizardView.providerList.waitForDisplayed();
await ApplicationWizardView.oauth2Provider.scrollIntoView();
await ApplicationWizardView.oauth2Provider.click();
await (await ApplicationWizardView.nextButton()).click();
await ApplicationWizardView.nextButton.click();
await ApplicationWizardView.pause();
await ApplicationWizardView.oauth.setAuthorizationFlow(EXPLICIT_CONSENT);
await (await ApplicationWizardView.nextButton()).click();
await ApplicationWizardView.nextButton.click();
await ApplicationWizardView.pause();
await expect(await getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
await expect(getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
});
it("Should configure a simple SAML Application", async () => {
await reachTheProvider("New SAML Application");
await (await ApplicationWizardView.providerList()).waitForDisplayed();
await (await ApplicationWizardView.samlProvider).scrollIntoView();
await (await ApplicationWizardView.samlProvider).click();
await ApplicationWizardView.providerList.waitForDisplayed();
await ApplicationWizardView.samlProvider.scrollIntoView();
await ApplicationWizardView.samlProvider.click();
await (await ApplicationWizardView.nextButton()).click();
await ApplicationWizardView.nextButton.click();
await ApplicationWizardView.pause();
await ApplicationWizardView.saml.setAuthorizationFlow(EXPLICIT_CONSENT);
await ApplicationWizardView.saml.acsUrl.setValue("http://example.com:8000/");
await (await ApplicationWizardView.nextButton()).click();
await ApplicationWizardView.nextButton.click();
await ApplicationWizardView.pause();
await expect(await getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
await expect(getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
});
it("Should configure a simple SCIM Application", async () => {
await reachTheProvider("New SCIM Application");
await (await ApplicationWizardView.providerList()).waitForDisplayed();
await (await ApplicationWizardView.scimProvider).scrollIntoView();
await (await ApplicationWizardView.scimProvider).click();
await ApplicationWizardView.providerList.waitForDisplayed();
await ApplicationWizardView.scimProvider.scrollIntoView();
await ApplicationWizardView.scimProvider.click();
await (await ApplicationWizardView.nextButton()).click();
await ApplicationWizardView.nextButton.click();
await ApplicationWizardView.pause();
await ApplicationWizardView.scim.url.setValue("http://example.com:8000/");
await ApplicationWizardView.scim.token.setValue("a-very-basic-token");
await (await ApplicationWizardView.nextButton()).click();
await ApplicationWizardView.nextButton.click();
await ApplicationWizardView.pause();
await expect(await getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
await expect(getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
});
it("Should configure a simple Radius Application", async () => {
await reachTheProvider("New Radius Application");
await (await ApplicationWizardView.providerList()).waitForDisplayed();
await (await ApplicationWizardView.radiusProvider).scrollIntoView();
await (await ApplicationWizardView.radiusProvider).click();
await ApplicationWizardView.providerList.waitForDisplayed();
await ApplicationWizardView.radiusProvider.scrollIntoView();
await ApplicationWizardView.radiusProvider.click();
await (await ApplicationWizardView.nextButton()).click();
await ApplicationWizardView.nextButton.click();
await ApplicationWizardView.pause();
await ApplicationWizardView.radius.setAuthenticationFlow("default-authentication-flow");
await (await ApplicationWizardView.nextButton()).click();
await ApplicationWizardView.nextButton.click();
await ApplicationWizardView.pause();
await expect(await getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
await expect(getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
});
it("Should configure a simple Transparent Proxy Application", async () => {
await reachTheProvider("New Transparent Proxy Application");
await (await ApplicationWizardView.providerList()).waitForDisplayed();
await (await ApplicationWizardView.proxyProviderProxy).scrollIntoView();
await (await ApplicationWizardView.proxyProviderProxy).click();
await (await ApplicationWizardView.nextButton()).click();
await ApplicationWizardView.providerList.waitForDisplayed();
await ApplicationWizardView.proxyProviderProxy.scrollIntoView();
await ApplicationWizardView.proxyProviderProxy.click();
await ApplicationWizardView.nextButton.click();
await ApplicationWizardView.pause();
await ApplicationWizardView.transparentProxy.setAuthorizationFlow(EXPLICIT_CONSENT);
@ -141,19 +139,19 @@ describe("Configure Applications with the Application Wizard", () => {
"http://internal.example.com",
);
await (await ApplicationWizardView.nextButton()).click();
await ApplicationWizardView.nextButton.click();
await ApplicationWizardView.pause();
await expect(await getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
await expect(getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
});
it("Should configure a simple Forward Proxy Application", async () => {
await reachTheProvider("New Forward Proxy Application");
await (await ApplicationWizardView.providerList()).waitForDisplayed();
await (await ApplicationWizardView.proxyProviderForwardsingle).scrollIntoView();
await (await ApplicationWizardView.proxyProviderForwardsingle).click();
await (await ApplicationWizardView.nextButton()).click();
await ApplicationWizardView.providerList.waitForDisplayed();
await ApplicationWizardView.proxyProviderForwardsingle.scrollIntoView();
await ApplicationWizardView.proxyProviderForwardsingle.click();
await ApplicationWizardView.nextButton.click();
await ApplicationWizardView.pause();
await ApplicationWizardView.forwardProxy.setAuthorizationFlow(EXPLICIT_CONSENT);
@ -161,9 +159,9 @@ describe("Configure Applications with the Application Wizard", () => {
"http://external.example.com",
);
await (await ApplicationWizardView.nextButton()).click();
await ApplicationWizardView.nextButton.click();
await ApplicationWizardView.pause();
await expect(await getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
await expect(getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
});
});

View File

@ -6,5 +6,5 @@ import { expect } from "@wdio/globals";
export const login = async () => {
await LoginPage.open();
await LoginPage.login(GOOD_USERNAME, GOOD_PASSWORD);
await expect(await UserLibraryPage.pageHeader()).toHaveText("My applications");
await expect(UserLibraryPage.pageHeader).toHaveText("My applications");
};

View File

@ -3,7 +3,7 @@
"moduleResolution": "node",
"module": "ESNext",
"target": "es2022",
"types": ["node", "@wdio/globals/types", "expect-webdriverio", "@wdio/mocha-framework", "@types/mocha"],
"types": ["node", "@wdio/globals/types", "expect-webdriverio", "@wdio/mocha-framework"],
"skipLibCheck": true,
"noEmit": true,
"allowImportingTsExtensions": true,

View File

@ -41,7 +41,6 @@ const definitions = {
const otherFiles = [
["node_modules/@patternfly/patternfly/patternfly.min.css", "."],
["node_modules/@patternfly/patternfly/patternfly-base.css", "."],
["node_modules/@patternfly/patternfly/assets/**", ".", "node_modules/@patternfly/patternfly/"],
["src/custom.css", "."],
["src/common/styles/**", "."],
@ -80,12 +79,6 @@ const interfaces = [
["polyfill/poly.ts", "."],
];
const extraTargets = [
["sdk/index.ts", "sdk", { entryNames: "[dir]/[name]" }],
["sdk/user-settings.ts", "sdk/user-settings", { entryNames: "[dir]/[name]" }],
["sdk/flow.ts", "sdk/flow", { entryNames: "[dir]/[name]" }],
];
const baseArgs = {
bundle: true,
write: true,
@ -108,11 +101,7 @@ function getVersion() {
return version;
}
function getAllTargets() {
return [...interfaces, ...extraTargets];
}
async function buildSingleTarget(source, dest, options) {
async function buildOneSource(source, dest) {
const DIST = path.join(__dirname, "./dist", dest);
console.log(`[${new Date(Date.now()).toISOString()}] Starting build for target ${source}`);
@ -123,7 +112,6 @@ async function buildSingleTarget(source, dest, options) {
entryPoints: [`./src/${source}`],
entryNames: `[dir]/[name]-${getVersion()}`,
outdir: DIST,
...options,
});
const end = Date.now();
console.log(
@ -136,10 +124,8 @@ async function buildSingleTarget(source, dest, options) {
}
}
async function buildTargets(targets) {
await Promise.allSettled(
targets.map(([source, dest, options]) => buildSingleTarget(source, dest, options)),
);
async function buildAuthentik(interfaces) {
await Promise.allSettled(interfaces.map(([source, dest]) => buildOneSource(source, dest)));
}
let timeoutId = null;
@ -149,7 +135,7 @@ function debouncedBuild() {
}
timeoutId = setTimeout(() => {
console.clear();
buildTargets(getAllTargets());
buildAuthentik(interfaces);
}, 250);
}
@ -157,7 +143,7 @@ if (process.argv.length > 2 && (process.argv[2] === "-h" || process.argv[2] ===
console.log(`Build the authentikUI
options:
-w, --watch: Build all ${getAllTargets().length} interfaces
-w, --watch: Build all ${interfaces.length} interfaces
-p, --proxy: Build only the polyfills and the loading application
-h, --help: This help message
`);
@ -177,11 +163,11 @@ if (process.argv.length > 2 && (process.argv[2] === "-w" || process.argv[2] ===
});
} else if (process.argv.length > 2 && (process.argv[2] === "-p" || process.argv[2] === "--proxy")) {
// There's no watch-for-proxy, sorry.
await buildTargets(
await buildAuthentik(
interfaces.filter(([_, dest]) => ["standalone/loading", "."].includes(dest)),
);
process.exit(0);
} else {
// And the fallback: just build it.
await buildTargets(interfaces);
await buildAuthentik(interfaces);
}

992
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,15 +11,15 @@
"@floating-ui/dom": "^1.6.9",
"@formatjs/intl-listformat": "^7.5.7",
"@fortawesome/fontawesome-free": "^6.6.0",
"@goauthentik/api": "^2024.8.0-1725367323",
"@goauthentik/api": "^2024.6.3-1724414734",
"@lit/context": "^1.1.2",
"@lit/localize": "^0.12.2",
"@lit/reactive-element": "^2.0.4",
"@lit/task": "^1.0.1",
"@open-wc/lit-helpers": "^0.7.0",
"@patternfly/elements": "^4.0.1",
"@patternfly/elements": "^4.0.0",
"@patternfly/patternfly": "^4.224.2",
"@sentry/browser": "^8.27.0",
"@sentry/browser": "^8.26.0",
"@webcomponents/webcomponentsjs": "^2.8.0",
"base64-js": "^1.5.1",
"chart.js": "^4.4.4",
@ -32,7 +32,7 @@
"guacamole-common-js": "^1.5.0",
"lit": "^3.2.0",
"md-front-matter": "^1.0.4",
"mermaid": "^11.1.0",
"mermaid": "^11.0.2",
"rapidoc": "^9.3.4",
"showdown": "^2.1.0",
"style-mod": "^4.1.2",
@ -57,7 +57,7 @@
"@jeysal/storybook-addon-css-user-preferences": "^0.2.0",
"@lit/localize-tools": "^0.8.0",
"@rollup/plugin-replace": "^5.0.7",
"@spotlightjs/spotlight": "^2.3.2",
"@spotlightjs/spotlight": "^2.3.0",
"@storybook/addon-essentials": "^8.2.9",
"@storybook/addon-links": "^8.2.9",
"@storybook/api": "^7.6.17",
@ -71,7 +71,6 @@
"@types/eslint__js": "^8.42.3",
"@types/grecaptcha": "^3.0.9",
"@types/guacamole-common-js": "1.5.2",
"@types/node": "^22.5.0",
"@types/showdown": "^2.0.6",
"@typescript-eslint/eslint-plugin": "^8.0.1",
"@typescript-eslint/parser": "^8.0.1",
@ -91,7 +90,6 @@
"github-slugger": "^2.0.0",
"glob": "^11.0.0",
"globals": "^15.9.0",
"knip": "^5.27.4",
"lit-analyzer": "^2.0.3",
"lockfile-lint": "^4.14.0",
"npm-run-all": "^4.1.5",
@ -109,7 +107,7 @@
"tslib": "^2.7.0",
"turnstile-types": "^1.2.2",
"typescript": "^5.5.4",
"typescript-eslint": "^8.4.0",
"typescript-eslint": "^8.2.0",
"vite-tsconfig-paths": "^5.0.1",
"wdio-wait-for": "^3.0.11",
"wireit": "^0.14.8"
@ -122,9 +120,9 @@
"@esbuild/darwin-arm64": "^0.23.0",
"@esbuild/linux-amd64": "^0.18.11",
"@esbuild/linux-arm64": "^0.23.0",
"@rollup/rollup-darwin-arm64": "4.21.2",
"@rollup/rollup-linux-arm64-gnu": "4.21.2",
"@rollup/rollup-linux-x64-gnu": "4.21.2"
"@rollup/rollup-darwin-arm64": "4.21.0",
"@rollup/rollup-linux-arm64-gnu": "4.21.0",
"@rollup/rollup-linux-x64-gnu": "4.21.0"
},
"private": true,
"scripts": {
@ -137,7 +135,6 @@
"extract-locales": "wireit",
"format": "wireit",
"lint": "wireit",
"lint:imports": "wireit",
"lint:lockfile": "wireit",
"lint:nightmare": "wireit",
"lint:package": "wireit",
@ -152,7 +149,8 @@
"storybook:build": "wireit",
"storybook:build-import-map": "wireit",
"test": "wireit",
"test-watch": "wireit",
"test-view": "wireit",
"test-watch": "npx wdio run ./wdio.conf.ts --autoCompileOpts.tsNodeOpts.project=tsconfig.test.json --watch",
"tsc": "wireit",
"watch": "run-s build-locales esbuild:watch"
},
@ -252,9 +250,6 @@
"lint:components": {
"command": "lit-analyzer src"
},
"lint:imports": {
"command": "knip --config scripts/knip.config.ts"
},
"lint:types": {
"command": "tsc --noEmit -p .",
"dependencies": [
@ -335,7 +330,7 @@
"TS_NODE_PROJECT": "tsconfig.test.json"
}
},
"test-watch": {
"test-view": {
"command": "wdio run ./wdio.conf.ts",
"env": {
"TS_NODE_PROJECT": "tsconfig.test.json"

View File

@ -14,18 +14,18 @@
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-swc": "^0.3.1",
"@swc/cli": "^0.4.0",
"@swc/core": "^1.7.23",
"@swc/core": "^1.7.18",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/jquery": "^3.5.30",
"lockfile-lint": "^4.14.0",
"prettier": "^3.3.2",
"rollup": "^4.21.2",
"rollup": "^4.21.0",
"rollup-plugin-copy": "^3.5.0",
"wireit": "^0.14.8"
},
"license": "MIT",
"optionalDependencies": {
"@swc/core": "^1.7.23",
"@swc/core": "^1.7.18",
"@swc/core-darwin-arm64": "^1.6.13",
"@swc/core-darwin-x64": "^1.6.13",
"@swc/core-linux-arm-gnueabihf": "^1.6.13",

View File

@ -1,48 +0,0 @@
import { type KnipConfig } from "knip";
const config: KnipConfig = {
"entry": [
"./src/admin/AdminInterface/AdminInterface.ts",
"./src/user/UserInterface.ts",
"./src/flow/FlowInterface.ts",
"./src/standalone/api-browser/index.ts",
"./src/enterprise/rac/index.ts",
"./src/standalone/loading/index.ts",
"./src/polyfill/poly.ts",
],
"project": ["src/**/*.ts", "src/**/*.js", "./scripts/*.mjs", ".storybook/*.ts"],
// "ignore": ["src/**/*.test.ts", "src/**/*.stories.ts"],
// Prevent Knip from complaining about web components, which export their classes but also
// export their registration, and we don't always use both.
"ignoreExportsUsedInFile": true,
"typescript": {
config: ["tsconfig.json"],
},
"wireit": {
config: ["package.json"],
},
"storybook": {
config: [".storybook/{main,test-runner}.{js,ts}"],
entry: [
".storybook/{manager,preview}.{js,jsx,ts,tsx}",
"**/*.@(mdx|stories.@(mdx|js|jsx|mjs|ts|tsx))",
],
project: [".storybook/**/*.{js,jsx,ts,tsx}"],
},
"eslint": {
entry: [
"eslint.config.mjs",
"scripts/eslint.precommit.mjs",
"scripts/eslint.nightmare.mjs",
"scripts/eslint-precommit.mjs",
"scripts/eslint-nightmare.mjs",
"scripts/eslint.mjs",
],
config: ["package.json"],
},
"webdriver-io": {
config: ["wdio.conf.js"],
},
};
export default config;

3057
web/sfe/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

28
web/sfe/package.json Normal file
View File

@ -0,0 +1,28 @@
{
"name": "@goauthentik/web-sfe",
"version": "0.0.0",
"private": true,
"license": "MIT",
"dependencies": {
"@goauthentik/api": "^2024.6.3-1724414734",
"base64-js": "^1.5.1",
"bootstrap": "^4.6.1",
"formdata-polyfill": "^4.0.10",
"jquery": "^3.7.1",
"weakmap-polyfill": "^2.0.4"
},
"scripts": {
"build": "rollup -c rollup.config.js --bundleConfigAsCjs",
"watch": "rollup -w -c rollup.config.js --bundleConfigAsCjs"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^26.0.1",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-swc": "^0.3.1",
"@swc/cli": "^0.4.0",
"@swc/core": "^1.7.18",
"@types/jquery": "^3.5.30",
"rollup": "^4.21.0",
"rollup-plugin-copy": "^3.5.0"
}
}

View File

@ -21,20 +21,10 @@ export class ApplicationWizardAuthenticationMethodChoice extends WithLicenseSumm
const selectedTypes = providerModelsList.filter(
(t) => t.formName === this.wizard.providerModel,
);
// As a hack, the Application wizard has separate provider paths for our three types of
// proxy providers. This patch swaps the form we want to be directed to on page 3 from the
// modelName to the formName, so we get the right one. This information isn't modified
// or forwarded, so the proxy-plus-subtype is correctly mapped on submission.
const typesForWizard = providerModelsList.map((provider) => ({
...provider,
modelName: provider.formName,
}));
return providerModelsList.length > 0
? html`<form class="pf-c-form pf-m-horizontal">
<ak-wizard-page-type-create
.types=${typesForWizard}
.types=${providerModelsList}
layout=${TypeCreateWizardPageLayouts.grid}
.selectedType=${selectedTypes.length > 0 ? selectedTypes[0] : undefined}
@select=${(ev: CustomEvent<LocalTypeCreate>) => {

View File

@ -1,5 +1,6 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import { parseAPIError } from "@goauthentik/common/errors";
import "@goauthentik/components/ak-radio-input";
import "@goauthentik/components/ak-switch-input";
import "@goauthentik/components/ak-text-input";
@ -24,7 +25,6 @@ import {
type TransactionApplicationRequest,
type TransactionApplicationResponse,
ValidationError,
ValidationErrorFromJSON,
} from "@goauthentik/api";
import BasePanel from "../BasePanel";
@ -133,9 +133,7 @@ export class ApplicationWizardCommitApplication extends BasePanel {
})
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.catch(async (resolution: any) => {
const errors = (this.errors = ValidationErrorFromJSON(
await resolution.response.json(),
));
const errors = await parseAPIError(resolution);
this.dispatchWizardUpdate({
update: {
...this.wizard,

View File

@ -11,7 +11,10 @@ import {
redirectUriHelp,
subjectModeOptions,
} from "@goauthentik/admin/providers/oauth2/OAuth2ProviderForm";
import { oauth2SourcesProvider } from "@goauthentik/admin/providers/oauth2/OAuth2Sources.js";
import {
makeSourceSelector,
oauth2SourcesProvider,
} from "@goauthentik/admin/providers/oauth2/OAuth2Sources.js";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { ascii_letters, digits, first, randomString } from "@goauthentik/common/utils";
import "@goauthentik/components/ak-number-input";
@ -263,12 +266,12 @@ export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel {
name="jwksSources"
.errorMessages=${errors?.jwksSources ?? []}
>
<ak-dual-select-provider
<ak-dual-select-dynamic-selected
.provider=${oauth2SourcesProvider}
.selected=${provider?.jwksSources ?? []}
.selector=${makeSourceSelector(provider?.jwksSources)}
available-label=${msg("Available Sources")}
selected-label=${msg("Selected Sources")}
></ak-dual-select-provider>
></ak-dual-select-dynamic-selected>
<p class="pf-c-form__helper-text">
${msg(
"JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider.",

View File

@ -1,5 +1,8 @@
import "@goauthentik/admin/applications/wizard/ak-wizard-title";
import { oauth2SourcesProvider } from "@goauthentik/admin/providers/oauth2/OAuth2Sources.js";
import {
makeSourceSelector,
oauth2SourcesProvider,
} from "@goauthentik/admin/providers/oauth2/OAuth2Sources.js";
import {
makeProxyPropertyMappingsSelector,
proxyPropertyMappingsProvider,
@ -11,7 +14,6 @@ import "@goauthentik/components/ak-text-input";
import "@goauthentik/components/ak-textarea-input";
import "@goauthentik/components/ak-toggle-group";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-provider.js";
import "@goauthentik/elements/forms/HorizontalFormElement";
import { msg } from "@lit/localize";
@ -228,12 +230,12 @@ export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel {
name="jwksSources"
.errorMessages=${errors?.jwksSources ?? []}
>
<ak-dual-select-provider
<ak-dual-select-dynamic-selected
.provider=${oauth2SourcesProvider}
.selected=${this.instance?.jwksSources ?? []}
.selector=${makeSourceSelector(this.instance?.jwksSources)}
available-label=${msg("Available Sources")}
selected-label=${msg("Selected Sources")}
></ak-dual-select-provider>
></ak-dual-select-dynamic-selected>
<p class="pf-c-form__helper-text">
${msg(
"JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider.",

View File

@ -1,5 +1,7 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { severityToLabel } from "@goauthentik/common/labels";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types";
import "@goauthentik/elements/forms/HorizontalFormElement";
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/Radio";
@ -16,6 +18,7 @@ import {
EventsApi,
Group,
NotificationRule,
NotificationTransport,
PaginatedNotificationTransportList,
SeverityEnum,
} from "@goauthentik/api";
@ -34,6 +37,13 @@ async function eventTransportsProvider(page = 1, search = "") {
};
}
export function makeTransportSelector(instanceTransports: string[] | undefined) {
const localTransports = instanceTransports ? new Set(instanceTransports) : undefined;
return localTransports
? ([pk, _]: DualSelectPair) => localTransports.has(pk)
: ([_0, _1, _2, stage]: DualSelectPair<NotificationTransport>) => stage !== undefined;
}
@customElement("ak-event-rule-form")
export class RuleForm extends ModelForm<NotificationRule, string> {
eventTransports?: PaginatedNotificationTransportList;
@ -114,12 +124,12 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
?required=${true}
name="transports"
>
<ak-dual-select-provider
<ak-dual-select-dynamic-selected
.provider=${eventTransportsProvider}
.selected=${this.instance?.transports}
.selector=${makeTransportSelector(this.instance?.transports)}
available-label="${msg("Available Transports")}"
selected-label="${msg("Selected Transports")}"
></ak-dual-select-provider>
></ak-dual-select-dynamic-selected>
<p class="pf-c-form__helper-text">
${msg(
"Select which transports should be used to notify the user. If none are selected, the notification will only be shown in the authentik UI.",

View File

@ -117,7 +117,7 @@ export class EventMatcherPolicyForm extends BasePolicyForm<EventMatcherPolicy> {
/>
<p class="pf-c-form__helper-text">
${msg(
"Matches Event's Client IP (strict matching, for network matching use an Expression Policy).",
"Matches Event's Client IP (strict matching, for network matching use an Expression Policy.",
)}
</p>
</ak-form-element-horizontal>

View File

@ -61,7 +61,9 @@ export class PolicyTestForm extends Form<PropertyMappingTestRequest> {
</ak-codemirror>`
: html` <div class="pf-c-form__group-label">
<div class="c-form__horizontal-group">
<span class="pf-c-form__label-text">${this.result?.result}</span>
<span class="pf-c-form__label-text">
<pre>${this.result?.result}</pre>
</span>
</div>
</div>`}
</ak-form-element-horizontal>`;

View File

@ -27,7 +27,7 @@ export class GoogleWorkspaceProviderGroupList extends Table<GoogleWorkspaceProvi
renderToolbar(): TemplateResult {
return html`<ak-forms-modal cancelText=${msg("Close")} ?closeAfterSuccessfulSubmit=${false}>
<span slot="submit">${msg("Sync")}</span>
<span slot="header">${msg("Sync User")}</span>
<span slot="header">${msg("Sync Group")}</span>
<ak-sync-object-form
.provider=${this.providerId}
model=${SyncObjectModelEnum.Group}

View File

@ -24,7 +24,7 @@ export class MicrosoftEntraProviderGroupList extends Table<MicrosoftEntraProvide
renderToolbar(): TemplateResult {
return html`<ak-forms-modal cancelText=${msg("Close")} ?closeAfterSuccessfulSubmit=${false}>
<span slot="submit">${msg("Sync")}</span>
<span slot="header">${msg("Sync User")}</span>
<span slot="header">${msg("Sync Group")}</span>
<ak-sync-object-form
.provider=${this.providerId}
model=${SyncObjectModelEnum.Group}

View File

@ -3,6 +3,12 @@ import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js";
import { PropertymappingsApi, ScopeMapping } from "@goauthentik/api";
export const defaultScopes = [
"goauthentik.io/providers/oauth2/scope-openid",
"goauthentik.io/providers/oauth2/scope-email",
"goauthentik.io/providers/oauth2/scope-profile",
];
export async function oauth2PropertyMappingsProvider(page = 1, search = "") {
const propertyMappings = await new PropertymappingsApi(
DEFAULT_CONFIG,
@ -23,6 +29,5 @@ export function makeOAuth2PropertyMappingsSelector(instanceMappings: string[] |
return localMappings
? ([pk, _]: DualSelectPair) => localMappings.has(pk)
: ([_0, _1, _2, scope]: DualSelectPair<ScopeMapping>) =>
scope?.managed?.startsWith("goauthentik.io/providers/oauth2/scope-") &&
scope?.managed !== "goauthentik.io/providers/oauth2/scope-offline_access";
scope?.managed && defaultScopes.includes(scope?.managed);
}

View File

@ -32,7 +32,7 @@ import {
makeOAuth2PropertyMappingsSelector,
oauth2PropertyMappingsProvider,
} from "./OAuth2PropertyMappings.js";
import { oauth2SourcesProvider } from "./OAuth2Sources.js";
import { makeSourceSelector, oauth2SourcesProvider } from "./OAuth2Sources.js";
export const clientTypeOptions = [
{
@ -52,12 +52,6 @@ export const clientTypeOptions = [
},
];
export const defaultScopes = [
"goauthentik.io/providers/oauth2/scope-openid",
"goauthentik.io/providers/oauth2/scope-email",
"goauthentik.io/providers/oauth2/scope-profile",
];
export const subjectModeOptions = [
{
label: msg("Based on the User's hashed ID"),
@ -335,12 +329,12 @@ export class OAuth2ProviderFormPage extends BaseProviderForm<OAuth2Provider> {
label=${msg("Trusted OIDC Sources")}
name="jwksSources"
>
<ak-dual-select-provider
<ak-dual-select-dynamic-selected
.provider=${oauth2SourcesProvider}
.selected=${provider?.jwksSources}
.selector=${makeSourceSelector(provider?.jwksSources)}
available-label=${msg("Available Sources")}
selected-label=${msg("Selected Sources")}
></ak-dual-select-provider>
></ak-dual-select-dynamic-selected>
<p class="pf-c-form__helper-text">
${msg(
"JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider.",

View File

@ -1,6 +1,7 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types";
import { SourcesApi } from "@goauthentik/api";
import { OAuthSource, SourcesApi } from "@goauthentik/api";
export async function oauth2SourcesProvider(page = 1, search = "") {
const oauthSources = await new SourcesApi(DEFAULT_CONFIG).sourcesOauthList({
@ -19,3 +20,11 @@ export async function oauth2SourcesProvider(page = 1, search = "") {
]),
};
}
export function makeSourceSelector(instanceSources: string[] | undefined) {
const localSources = instanceSources ? new Set(instanceSources) : undefined;
return localSources
? ([pk, _]: DualSelectPair) => localSources.has(pk)
: ([_0, _1, _2, prompt]: DualSelectPair<OAuthSource>) => prompt !== undefined;
}

View File

@ -1,12 +1,14 @@
import "@goauthentik/admin/common/ak-crypto-certificate-search";
import "@goauthentik/admin/common/ak-flow-search/ak-flow-search";
import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm";
import { oauth2SourcesProvider } from "@goauthentik/admin/providers/oauth2/OAuth2Sources.js";
import {
makeSourceSelector,
oauth2SourcesProvider,
} from "@goauthentik/admin/providers/oauth2/OAuth2Sources.js";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/components/ak-toggle-group";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-provider.js";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
import "@goauthentik/elements/forms/SearchSelect";
@ -403,12 +405,12 @@ ${this.instance?.skipPathRegex}</textarea
label=${msg("Trusted OIDC Sources")}
name="jwksSources"
>
<ak-dual-select-provider
<ak-dual-select-dynamic-selected
.provider=${oauth2SourcesProvider}
.selected=${this.instance?.jwksSources}
.selector=${makeSourceSelector(this.instance?.jwksSources)}
available-label=${msg("Available Sources")}
selected-label=${msg("Selected Sources")}
></ak-dual-select-provider>
></ak-dual-select-dynamic-selected>
<p class="pf-c-form__helper-text">
${msg(
"JWTs signed by certificates configured in the selected sources can be used to authenticate to this provider.",

View File

@ -1,12 +1,14 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/forms/DeleteBulkForm";
import "@goauthentik/elements/forms/ModalForm";
import "@goauthentik/elements/sync/SyncObjectForm";
import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ProvidersApi, SCIMProviderGroup } from "@goauthentik/api";
import { ProvidersApi, SCIMProviderGroup, SyncObjectModelEnum } from "@goauthentik/api";
@customElement("ak-provider-scim-groups-list")
export class SCIMProviderGroupList extends Table<SCIMProviderGroup> {
@ -20,6 +22,22 @@ export class SCIMProviderGroupList extends Table<SCIMProviderGroup> {
checkbox = true;
clearOnRefresh = true;
renderToolbar(): TemplateResult {
return html`<ak-forms-modal cancelText=${msg("Close")} ?closeAfterSuccessfulSubmit=${false}>
<span slot="submit">${msg("Sync")}</span>
<span slot="header">${msg("Sync Group")}</span>
<ak-sync-object-form
.provider=${this.providerId}
model=${SyncObjectModelEnum.Group}
.sync=${new ProvidersApi(DEFAULT_CONFIG).providersScimSyncObjectCreate}
slot="form"
>
</ak-sync-object-form>
<button slot="trigger" class="pf-c-button pf-m-primary">${msg("Sync")}</button>
</ak-forms-modal>
${super.renderToolbar()}`;
}
renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk

View File

@ -1,12 +1,14 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/forms/DeleteBulkForm";
import "@goauthentik/elements/forms/ModalForm";
import "@goauthentik/elements/sync/SyncObjectForm";
import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ProvidersApi, SCIMProviderUser } from "@goauthentik/api";
import { ProvidersApi, SCIMProviderUser, SyncObjectModelEnum } from "@goauthentik/api";
@customElement("ak-provider-scim-users-list")
export class SCIMProviderUserList extends Table<SCIMProviderUser> {
@ -20,6 +22,22 @@ export class SCIMProviderUserList extends Table<SCIMProviderUser> {
checkbox = true;
clearOnRefresh = true;
renderToolbar(): TemplateResult {
return html`<ak-forms-modal cancelText=${msg("Close")} ?closeAfterSuccessfulSubmit=${false}>
<span slot="submit">${msg("Sync")}</span>
<span slot="header">${msg("Sync User")}</span>
<ak-sync-object-form
.provider=${this.providerId}
model=${SyncObjectModelEnum.User}
.sync=${new ProvidersApi(DEFAULT_CONFIG).providersScimSyncObjectCreate}
slot="form"
>
</ak-sync-object-form>
<button slot="trigger" class="pf-c-button pf-m-primary">${msg("Sync")}</button>
</ak-forms-modal>
${super.renderToolbar()}`;
}
renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk

View File

@ -7,9 +7,6 @@ export function renderSourceIcon(name: string, iconUrl: string | undefined | nul
const url = iconUrl.replaceAll("fa://", "");
return html`<i class="fas ${url}" title="${name}"></i>`;
}
if (window.authentik_sdk?.base) {
return html`<img src="${window.authentik_sdk?.base}${iconUrl}" alt="${name}" />`;
}
return html`<img src="${iconUrl}" alt="${name}" />`;
}
return icon;

View File

@ -2,7 +2,9 @@ import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import { deviceTypeRestrictionPair } from "@goauthentik/admin/stages/authenticator_webauthn/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/Alert";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-provider";
import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
import "@goauthentik/elements/forms/Radio";
@ -18,6 +20,7 @@ import {
DeviceClassesEnum,
NotConfiguredActionEnum,
PaginatedStageList,
Stage,
StagesApi,
UserVerificationEnum,
} from "@goauthentik/api";
@ -36,6 +39,14 @@ async function stagesProvider(page = 1, search = "") {
};
}
export function makeStageSelector(instanceStages: string[] | undefined) {
const localStages = instanceStages ? new Set(instanceStages) : undefined;
return localStages
? ([pk, _]: DualSelectPair) => localStages.has(pk)
: ([_0, _1, _2, stage]: DualSelectPair<Stage>) => stage !== undefined;
}
async function authenticatorWebauthnDeviceTypesListProvider(page = 1, search = "") {
const devicetypes = await new StagesApi(
DEFAULT_CONFIG,
@ -205,14 +216,14 @@ export class AuthenticatorValidateStageForm extends BaseStageForm<AuthenticatorV
label=${msg("Configuration stages")}
name="configurationStages"
>
<ak-dual-select-provider
<ak-dual-select-dynamic-selected
.provider=${stagesProvider}
.selected=${Array.from(
this.instance?.configurationStages ?? [],
.selector=${makeStageSelector(
this.instance?.configurationStages,
)}
available-label="${msg("Available Stages")}"
selected-label="${msg("Selected Stages")}"
></ak-dual-select-provider>
></ak-dual-select-dynamic-selected>
<p class="pf-c-form__helper-text">
${msg(
"Stages used to configure Authenticator when user doesn't have any compatible devices. After this configuration Stage passes, the user is not prompted again.",

View File

@ -41,7 +41,7 @@ async function sourcesProvider(page = 1, search = "") {
};
}
async function makeSourcesSelector(instanceSources: string[] | undefined) {
function makeSourcesSelector(instanceSources: string[] | undefined) {
const localSources = instanceSources ? new Set(instanceSources) : undefined;
return localSources

View File

@ -1,4 +1,5 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { parseAPIError } from "@goauthentik/common/errors";
import { first } from "@goauthentik/common/utils";
import "@goauthentik/elements/CodeMirror";
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
@ -22,7 +23,7 @@ import {
PromptTypeEnum,
ResponseError,
StagesApi,
ValidationErrorFromJSON,
ValidationError,
} from "@goauthentik/api";
class PreviewStageHost implements StageHost {
@ -83,10 +84,8 @@ export class PromptForm extends ModelForm<Prompt, string> {
});
this.previewError = undefined;
} catch (exc) {
const errorMessage = ValidationErrorFromJSON(
await (exc as ResponseError).response.json(),
);
this.previewError = errorMessage.nonFieldErrors;
const errorMessage = parseAPIError(exc as ResponseError);
this.previewError = (errorMessage as ValidationError).nonFieldErrors;
}
}

View File

@ -2,6 +2,8 @@ import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
import "@goauthentik/admin/stages/prompt/PromptForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { PFSize } from "@goauthentik/common/enums";
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
import "@goauthentik/elements/forms/ModalForm";
@ -11,9 +13,9 @@ import { TemplateResult, html, nothing } from "lit";
import { customElement } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { PoliciesApi, PromptStage, StagesApi } from "@goauthentik/api";
import { PoliciesApi, Policy, Prompt, PromptStage, StagesApi } from "@goauthentik/api";
async function promptsProvider(page = 1, search = "") {
async function promptFieldsProvider(page = 1, search = "") {
const prompts = await new StagesApi(DEFAULT_CONFIG).stagesPromptPromptsList({
ordering: "field_name",
pageSize: 20,
@ -25,11 +27,19 @@ async function promptsProvider(page = 1, search = "") {
pagination: prompts.pagination,
options: prompts.results.map((prompt) => [
prompt.pk,
str`${prompt.name} ("${prompt.fieldKey}", of type ${prompt.type})`,
msg(str`${prompt.name} ("${prompt.fieldKey}", of type ${prompt.type})`),
]),
};
}
function makeFieldSelector(instanceFields: string[] | undefined) {
const localFields = instanceFields ? new Set(instanceFields) : undefined;
return localFields
? ([pk, _]: DualSelectPair) => localFields.has(pk)
: ([_0, _1, _2, prompt]: DualSelectPair<Prompt>) => prompt !== undefined;
}
async function policiesProvider(page = 1, search = "") {
const policies = await new PoliciesApi(DEFAULT_CONFIG).policiesAllList({
ordering: "name",
@ -47,6 +57,14 @@ async function policiesProvider(page = 1, search = "") {
};
}
function makePoliciesSelector(instancePolicies: string[] | undefined) {
const localPolicies = instancePolicies ? new Set(instancePolicies) : undefined;
return localPolicies
? ([pk, _]: DualSelectPair) => localPolicies.has(pk)
: ([_0, _1, _2, policy]: DualSelectPair<Policy>) => policy !== undefined;
}
@customElement("ak-stage-prompt-form")
export class PromptStageForm extends BaseStageForm<PromptStage> {
loadInstance(pk: string): Promise<PromptStage> {
@ -90,12 +108,12 @@ export class PromptStageForm extends BaseStageForm<PromptStage> {
?required=${true}
name="fields"
>
<ak-dual-select-provider
.provider=${promptsProvider}
.selected=${this.instance?.fields}
<ak-dual-select-dynamic-selected
.provider=${promptFieldsProvider}
.selector=${makeFieldSelector(this.instance?.fields)}
available-label="${msg("Available Fields")}"
selected-label="${msg("Selected Fields")}"
></ak-dual-select-provider>
></ak-dual-select-dynamic-selected>
${this.instance
? html`<ak-forms-modal size=${PFSize.XLarge}>
<span slot="submit"> ${msg("Create")} </span>
@ -115,12 +133,12 @@ export class PromptStageForm extends BaseStageForm<PromptStage> {
label=${msg("Validation Policies")}
name="validationPolicies"
>
<ak-dual-select-provider
<ak-dual-select-dynamic-selected
.provider=${policiesProvider}
.selected=${this.instance?.validationPolicies}
.selector=${makePoliciesSelector(this.instance?.validationPolicies)}
available-label="${msg("Available Fields")}"
selected-label="${msg("Selected Fields")}"
></ak-dual-select-provider>
></ak-dual-select-dynamic-selected>
<p class="pf-c-form__helper-text">
${msg(
"Selected policies are executed when the stage is submitted to validate the data.",

View File

@ -2,7 +2,6 @@ import {
CSRFMiddleware,
EventMiddleware,
LoggingMiddleware,
SDKMiddleware,
} from "@goauthentik/common/api/middleware";
import { EVENT_LOCALE_REQUEST, VERSION } from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global";
@ -68,18 +67,8 @@ export function getMetaContent(key: string): string {
return metaEl.content;
}
export function apiBase(): string {
if (process.env.AK_API_BASE_PATH) {
return process.env.AK_API_BASE_PATH;
}
if (window.authentik_sdk?.base) {
return window.authentik_sdk?.base;
}
return window.location.origin;
}
export const DEFAULT_CONFIG = new Configuration({
basePath: `${apiBase()}/api/v3`,
basePath: (process.env.AK_API_BASE_PATH || window.location.origin) + "/api/v3",
headers: {
"sentry-trace": getMetaContent("sentry-trace"),
},
@ -87,7 +76,6 @@ export const DEFAULT_CONFIG = new Configuration({
new CSRFMiddleware(),
new EventMiddleware(),
new LoggingMiddleware(globalAK().brand),
new SDKMiddleware(),
],
});

View File

@ -44,21 +44,6 @@ export class CSRFMiddleware implements Middleware {
}
}
export class SDKMiddleware implements Middleware {
token?: string;
constructor() {
this.token = window.authentik_sdk?.token;
}
pre?(context: RequestContext): Promise<FetchParams | void> {
if (this.token) {
context.init.credentials = "include";
// @ts-ignore
context.init.headers["Authorization"] = `Bearer ${this.token}`;
}
return Promise.resolve(context);
}
}
export class EventMiddleware implements Middleware {
post?(context: ResponseContext): Promise<Response | void> {
const request: RequestInfo = {

View File

@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
export const ERROR_CLASS = "pf-m-danger";
export const PROGRESS_CLASS = "pf-m-in-progress";
export const CURRENT_CLASS = "pf-m-current";
export const VERSION = "2024.8.0";
export const VERSION = "2024.8.1";
export const TITLE_DEFAULT = "authentik";
export const ROUTE_SEPARATOR = ";";

View File

@ -12,11 +12,17 @@ export class RequestError extends Error {}
export type APIErrorTypes = ValidationError | GenericError;
export const HTTP_BAD_REQUEST = 400;
export const HTTP_INTERNAL_SERVICE_ERROR = 500;
export async function parseAPIError(error: Error): Promise<APIErrorTypes> {
if (!(error instanceof ResponseError)) {
return error;
}
if (error.response.status < 400 || error.response.status > 499) {
if (
error.response.status < HTTP_BAD_REQUEST ||
error.response.status >= HTTP_INTERNAL_SERVICE_ERROR
) {
return error;
}
const body = await error.response.json();

View File

@ -1,11 +1,4 @@
import {
Config,
ConfigFromJSON,
CurrentBrand,
CurrentBrandFromJSON,
ErrorReportingConfigFromJSON,
UiThemeEnum,
} from "@goauthentik/api";
import { Config, ConfigFromJSON, CurrentBrand, CurrentBrandFromJSON } from "@goauthentik/api";
export interface GlobalAuthentik {
_converted?: boolean;
@ -35,12 +28,9 @@ export function globalAK(): GlobalAuthentik {
return {
config: ConfigFromJSON({
capabilities: [],
error_reporting: ErrorReportingConfigFromJSON({}),
}),
brand: CurrentBrandFromJSON({
matched_domain: window.location.host,
ui_footer_links: [],
ui_theme: window.authentik_sdk?.forceTheme ?? UiThemeEnum.Automatic,
}),
versionFamily: "",
versionSubdomain: "",
@ -50,10 +40,6 @@ export function globalAK(): GlobalAuthentik {
return ak;
}
export function isEmbedded() {
return !!window.authentik_sdk;
}
export function docLink(path: string): string {
const ak = globalAK();
// Default case or beta build which should always point to latest

View File

@ -21,12 +21,9 @@ export class WebsocketClient {
connect(): void {
if (navigator.webdriver) return;
let wsUrl = `${window.location.protocol.replace("http", "ws")}//${
const wsUrl = `${window.location.protocol.replace("http", "ws")}//${
window.location.host
}/ws/client/`;
if (window.authentik_sdk?.base) {
wsUrl = `${window.authentik_sdk?.base.replace("http", "ws")}/ws/client/`;
}
this.messageSocket = new WebSocket(wsUrl);
this.messageSocket.addEventListener("open", () => {
console.debug(`authentik/ws: connected to ${wsUrl}`);

View File

@ -1,11 +1,10 @@
import { isEmbedded } from "@goauthentik/common/global";
import { UIConfig, uiConfig } from "@goauthentik/common/ui/config";
import { ModalOrchestrationController } from "@goauthentik/elements/controllers/ModalOrchestrationController.js";
import { ensureCSSStyleSheet } from "@goauthentik/elements/utils/ensureCSSStyleSheet";
import { state } from "lit/decorators.js";
import PFVariables from "@patternfly/patternfly/base/patternfly-variables.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import type { Config, CurrentBrand, LicenseSummary } from "@goauthentik/api";
import { UiThemeEnum } from "@goauthentik/api";
@ -44,10 +43,7 @@ export class Interface extends AKElement implements AkInterface {
constructor() {
super();
document.adoptedStyleSheets = [
...document.adoptedStyleSheets,
ensureCSSStyleSheet(PFVariables),
];
document.adoptedStyleSheets = [...document.adoptedStyleSheets, ensureCSSStyleSheet(PFBase)];
this[brandContext] = new BrandContextController(this);
this[configContext] = new ConfigContextController(this);
this[modalController] = new ModalOrchestrationController(this);
@ -65,9 +61,7 @@ export class Interface extends AKElement implements AkInterface {
// Instead of calling ._activateTheme() twice, we insert the root document in the call
// since multiple calls to ._activateTheme() would not do anything after the first call
// as the theme is already enabled.
if (!isEmbedded()) {
roots.unshift(document as unknown as DocumentOrShadowRoot);
}
roots.unshift(document as unknown as DocumentOrShadowRoot);
super._activateTheme(theme, ...roots);
}

View File

@ -1,4 +1,5 @@
import { EVENT_REFRESH } from "@goauthentik/common/constants";
import { parseAPIError } from "@goauthentik/common/errors";
import { MessageLevel } from "@goauthentik/common/messages";
import { camelToSnake, convertToSlug, dateToUTC } from "@goauthentik/common/utils";
import { AKElement } from "@goauthentik/elements/Base";
@ -6,6 +7,7 @@ import { HorizontalFormElement } from "@goauthentik/elements/forms/HorizontalFor
import { PreventFormSubmit } from "@goauthentik/elements/forms/helpers";
import { showMessage } from "@goauthentik/elements/messages/MessageContainer";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit";
import { property, state } from "lit/decorators.js";
@ -18,7 +20,7 @@ import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-gro
import PFSwitch from "@patternfly/patternfly/components/Switch/switch.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { ResponseError, ValidationError, ValidationErrorFromJSON } from "@goauthentik/api";
import { ResponseError, ValidationError, instanceOfValidationError } from "@goauthentik/api";
export class APIError extends Error {
constructor(public response: ValidationError) {
@ -124,9 +126,6 @@ export function serializeForm<T extends KeyUnknown>(
return json as unknown as T;
}
const HTTP_BAD_REQUEST = 400;
const HTTP_INTERNAL_SERVICE_ERROR = 500;
/**
* Form
*
@ -307,18 +306,9 @@ export abstract class Form<T> extends AKElement {
return response;
} catch (ex) {
if (ex instanceof ResponseError) {
let msg = ex.response.statusText;
if (
ex.response.status >= HTTP_BAD_REQUEST &&
ex.response.status < HTTP_INTERNAL_SERVICE_ERROR
) {
const errorMessage = ValidationErrorFromJSON(await ex.response.json());
if (!errorMessage) {
return errorMessage;
}
if (errorMessage instanceof Error) {
throw errorMessage;
}
let errorMessage = ex.response.statusText;
const error = await parseAPIError(ex);
if (instanceOfValidationError(error)) {
// assign all input-related errors to their elements
const elements =
this.shadowRoot?.querySelectorAll<HorizontalFormElement>(
@ -330,26 +320,28 @@ export abstract class Form<T> extends AKElement {
if (!elementName) {
return;
}
if (camelToSnake(elementName) in errorMessage) {
element.errorMessages = errorMessage[camelToSnake(elementName)];
if (camelToSnake(elementName) in error) {
element.errorMessages = (error as ValidationError)[
camelToSnake(elementName)
];
element.invalid = true;
} else {
element.errorMessages = [];
element.invalid = false;
}
});
if (errorMessage.nonFieldErrors) {
this.nonFieldErrors = errorMessage.nonFieldErrors;
if ((error as ValidationError).nonFieldErrors) {
this.nonFieldErrors = (error as ValidationError).nonFieldErrors;
}
errorMessage = msg("Invalid update request.");
// Only change the message when we have `detail`.
// Everything else is handled in the form.
if ("detail" in errorMessage) {
msg = errorMessage.detail;
if ("detail" in (error as ValidationError)) {
errorMessage = (error as ValidationError).detail;
}
}
// error is local or not from rest_framework
showMessage({
message: msg,
message: errorMessage,
level: MessageLevel.error,
});
}

View File

@ -9,6 +9,7 @@ import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/messages/Message";
import { APIMessage } from "@goauthentik/elements/messages/Message";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement, property } from "lit/decorators.js";
@ -20,6 +21,9 @@ export function showMessage(message: APIMessage, unique = false): void {
if (!container) {
throw new SentryIgnoredError("failed to find message container");
}
if (message.message.trim() === "") {
message.message = msg("Error");
}
container.addMessage(message, unique);
container.requestUpdate();
}

View File

@ -69,19 +69,9 @@ export class TypeCreateWizardPage extends WithLicenseSummary(WizardPage) {
}
renderGrid(): TemplateResult {
return html`<div
class="pf-l-grid pf-m-gutter"
data-ouid-component-type="ak-type-create-grid"
>
return html`<div class="pf-l-grid pf-m-gutter">
${this.types.map((type, idx) => {
const requiresEnterprise = type.requiresEnterprise && !this.hasEnterpriseLicense;
// It's valid to pass in a local modelName or the full name with application
// part. If the latter, we only want the part after the dot to appear as our
// OUIA tag for test automation.
const componentName = type.modelName.includes(".")
? (type.modelName.split(".")[1] ?? "--unknown--")
: type.modelName;
return html`<div
class="pf-l-grid__item pf-m-3-col pf-c-card ${requiresEnterprise
? "pf-m-non-selectable-raised"
@ -89,8 +79,6 @@ export class TypeCreateWizardPage extends WithLicenseSummary(WizardPage) {
? "pf-m-selected-raised"
: ""}"
tabindex=${idx}
data-ouid-component-type="ak-type-create-grid-card"
data-ouid-component-name=${componentName}
@click=${() => {
if (requiresEnterprise) {
return;
@ -119,17 +107,10 @@ export class TypeCreateWizardPage extends WithLicenseSummary(WizardPage) {
}
renderList(): TemplateResult {
return html`<form
class="pf-c-form pf-m-horizontal"
data-ouid-component-type="ak-type-create-list"
>
return html`<form class="pf-c-form pf-m-horizontal">
${this.types.map((type) => {
const requiresEnterprise = type.requiresEnterprise && !this.hasEnterpriseLicense;
return html`<div
class="pf-c-radio"
data-ouid-component-type="ak-type-create-list-card"
data-ouid-component-name=${type.modelName.split(".")[1] ?? "--unknown--"}
>
return html`<div class="pf-c-radio">
<input
class="pf-c-radio__input"
type="radio"

View File

@ -4,7 +4,7 @@ import {
EVENT_FLOW_INSPECTOR_TOGGLE,
TITLE_DEFAULT,
} from "@goauthentik/common/constants";
import { globalAK, isEmbedded } from "@goauthentik/common/global";
import { globalAK } from "@goauthentik/common/global";
import { configureSentry } from "@goauthentik/common/sentry";
import { first } from "@goauthentik/common/utils";
import { WebsocketClient } from "@goauthentik/common/ws";
@ -84,7 +84,6 @@ export class FlowExecutor extends Interface implements StageHost {
return [PFBase, PFLogin, PFDrawer, PFButton, PFTitle, PFList, PFBackgroundImage].concat(css`
:host {
--pf-c-login__main-body--PaddingBottom: var(--pf-global--spacer--2xl);
position: relative;
}
.pf-c-background-image::before {
--pf-c-background-image--BackgroundImage: var(--ak-flow-background);
@ -96,6 +95,9 @@ export class FlowExecutor extends Interface implements StageHost {
.ak-hidden {
display: none;
}
:host {
position: relative;
}
.pf-c-drawer__content {
background-color: transparent;
}
@ -260,13 +262,10 @@ export class FlowExecutor extends Interface implements StageHost {
this.challenge = challenge as ChallengeTypes;
}
setBackgroundImage(value: ContextualFlowInfo) {
setShadowStyles(value: ContextualFlowInfo) {
if (!value) {
return;
}
if (isEmbedded()) {
return;
}
this.shadowRoot
?.querySelectorAll<HTMLDivElement>(".pf-c-background-image")
.forEach((bg) => {
@ -277,7 +276,7 @@ export class FlowExecutor extends Interface implements StageHost {
// DOM post-processing has to happen after the render.
updated(changedProperties: PropertyValues<this>) {
if (changedProperties.has("flowInfo") && this.flowInfo !== undefined) {
this.setBackgroundImage(this.flowInfo);
this.setShadowStyles(this.flowInfo);
}
}
@ -460,63 +459,56 @@ export class FlowExecutor extends Interface implements StageHost {
}
}
renderCard() {
return html`<div class="pf-c-login ${this.getLayout()}">
<div class="${this.getLayoutClass()}">
<div class="pf-c-login__main">
${this.loading && this.challenge
? html`<ak-loading-overlay></ak-loading-overlay>`
: nothing}
${isEmbedded()
? nothing
: html` <div class="pf-c-login__main-header pf-c-brand ak-brand">
<img
src="${themeImage(
first(
this.brand?.brandingLogo,
globalAK()?.brand.brandingLogo,
DefaultBrand.brandingLogo,
),
)}"
alt="authentik Logo"
/>
</div>`}
${until(this.renderChallenge())}
</div>
${isEmbedded()
? nothing
: html` <footer class="pf-c-login__footer">
<ul class="pf-c-list pf-m-inline">
${this.brand?.uiFooterLinks?.map((link) => {
if (link.href) {
return html`<li>
<a href="${link.href}">${link.name}</a>
</li>`;
}
return html`<li>
<span>${link.name}</span>
</li>`;
})}
<li>
<span>${msg("Powered by authentik")}</span>
</li>
</ul>
</footer>`}
</div>
</div>`;
}
render(): TemplateResult {
if (isEmbedded()) {
return this.renderCard();
}
return html` <ak-locale-context>
<div class="pf-c-background-image"></div>
<div class="pf-c-page__drawer">
<div class="pf-c-drawer ${this.inspectorOpen ? "pf-m-expanded" : "pf-m-collapsed"}">
<div class="pf-c-drawer__main">
<div class="pf-c-drawer__content">
<div class="pf-c-drawer__body">${this.renderCard()}</div>
<div class="pf-c-drawer__body">
<div class="pf-c-login ${this.getLayout()}">
<div class="${this.getLayoutClass()}">
<div class="pf-c-login__main">
${this.loading && this.challenge
? html`<ak-loading-overlay></ak-loading-overlay>`
: nothing}
<div
class="pf-c-login__main-header pf-c-brand ak-brand"
>
<img
src="${themeImage(
first(
this.brand?.brandingLogo,
globalAK()?.brand.brandingLogo,
DefaultBrand.brandingLogo,
),
)}"
alt="authentik Logo"
/>
</div>
${until(this.renderChallenge())}
</div>
<footer class="pf-c-login__footer">
<ul class="pf-c-list pf-m-inline">
${this.brand?.uiFooterLinks?.map((link) => {
if (link.href) {
return html`<li>
<a href="${link.href}">${link.name}</a>
</li>`;
}
return html`<li>
<span>${link.name}</span>
</li>`;
})}
<li>
<span>${msg("Powered by authentik")}</span>
</li>
</ul>
</footer>
</div>
</div>
</div>
</div>
${until(this.renderInspector())}
</div>

View File

@ -99,7 +99,6 @@ export class IdentificationStage extends BaseStage<
createHelperForm(): void {
const compatMode = "ShadyDOM" in window;
this.form = document.createElement("form");
this.form.style.display = "none";
document.documentElement.appendChild(this.form);
// Only add the additional username input if we're in a shadow dom
// otherwise it just confuses browsers

8
web/src/global.d.ts vendored
View File

@ -12,11 +12,3 @@ declare namespace Intl {
public format: (items: string[]) => string;
}
}
declare interface Window {
authentik_sdk?: {
base: string;
token?: string;
forceTheme?: string;
};
}

View File

@ -1,19 +0,0 @@
import { Interface } from "@goauthentik/elements/Interface/Interface";
import { html } from "lit";
import { customElement } from "lit/decorators.js";
import { UiThemeEnum, UiThemeEnumFromJSON } from "@goauthentik/api";
@customElement("ak-sdk-interface")
export class SDKInterface extends Interface {
constructor() {
super();
}
render() {
return html`<slot></slot>`;
}
async getTheme(): Promise<UiThemeEnum> {
return UiThemeEnumFromJSON(window.authentik_sdk?.forceTheme) || UiThemeEnum.Automatic;
}
}

View File

@ -1,34 +0,0 @@
import "@goauthentik/elements/messages/MessageContainer";
import { FlowExecutor } from "@goauthentik/flow/FlowExecutor";
// Statically import some stages to speed up load speed
import "@goauthentik/flow/stages/access_denied/AccessDeniedStage";
// Import webauthn-related stages to prevent issues on safari
// Which is overly sensitive to allowing things only in the context of a
// user interaction
import "@goauthentik/flow/stages/authenticator_validate/AuthenticatorValidateStage";
import "@goauthentik/flow/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage";
import "@goauthentik/flow/stages/autosubmit/AutosubmitStage";
import "@goauthentik/flow/stages/captcha/CaptchaStage";
import "@goauthentik/flow/stages/identification/IdentificationStage";
import "@goauthentik/flow/stages/password/PasswordStage";
import "@goauthentik/sdk/common";
// end of stage import
import { html, nothing } from "lit";
import { customElement } from "lit/decorators.js";
import { until } from "lit/directives/until.js";
@customElement("ak-embedded-flow-executor")
export class EmbeddedFlowExecutor extends FlowExecutor {
renderCard() {
return html`<div class="pf-c-login">
<div class="pf-c-login__main">
${this.loading && this.challenge
? html`<ak-loading-overlay></ak-loading-overlay>`
: nothing}
${until(this.renderChallenge())}
</div>
</div>`;
}
}

View File

@ -1,2 +0,0 @@
import "@goauthentik/sdk/flow";
import "@goauthentik/sdk/user-settings";

View File

@ -1,3 +0,0 @@
import "@goauthentik/elements/messages/MessageContainer";
import "@goauthentik/sdk/common";
import "@goauthentik/user/user-settings/UserSettingsPage";

View File

@ -21,7 +21,6 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
import {
ChallengeTypes,
CoreApi,
FlowChallengeResponseRequest,
FlowErrorChallenge,
FlowsApi,
@ -83,11 +82,8 @@ export class UserSettingsFlowExecutor
});
}
async firstUpdated(): Promise<void> {
if (!this.brand) {
this.brand = await new CoreApi(DEFAULT_CONFIG).coreBrandsCurrentRetrieve();
}
this.flowSlug = this.brand.flowUserSettings;
firstUpdated(): void {
this.flowSlug = this.brand?.flowUserSettings;
if (!this.flowSlug) {
return;
}

View File

@ -125,8 +125,10 @@ export class MFADevicesPage extends Table<Device> {
return [
html`${item.name}`,
html`${deviceTypeName(item)}`,
html`<div>${getRelativeTime(item.created)}</div>
<small>${item.created.toLocaleString()}</small>`,
html`${item.created.getTime() > 0
? html`<div>${getRelativeTime(item.created)}</div>
<small>${item.created.toLocaleString()}</small>`
: html`-`}`,
html`${item.lastUsed
? html`<div>${getRelativeTime(item.lastUsed)}</div>
<small>${item.lastUsed.toLocaleString()}</small>`

View File

@ -10,7 +10,6 @@
"@goauthentik/flow/*": ["./src/flow/*"],
"@goauthentik/locales/*": ["./src/locales/*"],
"@goauthentik/polyfill/*": ["./src/polyfill/*"],
"@goauthentik/sdk/*": ["./src/sdk/*"],
"@goauthentik/standalone/*": ["./src/standalone/*"],
"@goauthentik/user/*": ["./src/user/*"]
}

View File

@ -8,12 +8,6 @@ import tsconfigPaths from "vite-tsconfig-paths";
const isProdBuild = process.env.NODE_ENV === "production";
const apiBasePath = process.env.AK_API_BASE_PATH || "";
const runHeadless = process.env.CI !== undefined;
const maxInstances =
process.env.MAX_INSTANCES !== undefined
? parseInt(process.env.MAX_INSTANCES, 10)
: runHeadless
? 10
: 1;
export const config: Options.Testrunner = {
//
@ -87,7 +81,7 @@ export const config: Options.Testrunner = {
// and 30 processes will get spawned. The property handles how many capabilities
// from the same test should run tests.
//
maxInstances,
maxInstances: 10,
//
// If you have trouble getting all important capabilities together, check out the
// Sauce Labs platform configurator - a great tool to configure your capabilities:

View File

@ -1831,6 +1831,10 @@
<source>Match created events with this action type. When left empty, all action types will be matched.</source>
<target>Ordnen Sie erstellte Ereignisse diesem Aktionstyp zu. Wenn es leer gelassen wird, werden alle Aktionstypen abgeglichen.</target>
</trans-unit>
<trans-unit id="sfab527528ea64618">
<source>Matches Event's Client IP (strict matching, for network matching use an Expression Policy.</source>
<target>Stimmt mit der Client-IP des Ereignisses überein (strenge Übereinstimmung, verwenden Sie für die Netzwerkübereinstimmung eine Ausdrucksrichtlinie.</target>
</trans-unit>
<trans-unit id="s5a15a8f39c699273">
<source>Match events created by selected application. When left empty, all applications are matched.</source>
<target>Übereinstimmungsereignisse, die von der ausgewählten Anwendung erstellt wurden. Wenn es leer gelassen wird, werden alle Anwendungen abgeglichen.</target>
@ -6886,9 +6890,6 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sae72e1569d34fb02">
<source>External user usage</source>
</trans-unit>
<trans-unit id="sa82f8948649d0989">
<source>Matches Event's Client IP (strict matching, for network matching use an Expression Policy).</source>
</trans-unit>
</body>
</file>

Some files were not shown because too many files have changed in this diff Show More