Merge branch 'main' into web/wdio-2

* main: (50 commits)
  web/admin: invitation stage: default "continue without invitation" to false
  core: bump pydantic from 2.4.1 to 2.4.2 (#7014)
  website: bump postcss from 8.4.30 to 8.4.31 in /website (#7015)
  internal: fix redis session store (#7011)
  web: bump rollup from 3.29.3 to 3.29.4 in /web (#7009)
  core: bump github.com/prometheus/client_golang from 1.16.0 to 1.17.0 (#7007)
  web: bump the esbuild group in /web with 2 updates (#7005)
  website: bump react-tooltip from 5.21.4 to 5.21.5 in /website (#7006)
  core: bump github.com/redis/go-redis/v9 from 9.2.0 to 9.2.1 (#7008)
  root: fix missing /lifecycle in path
  website/blog: add info-block to blog about m2m (#7002)
  root: handle SIGHUP and SIGUSR2, healthcheck gunicorn (#6630)
  flows: stage_invalid() makes flow restart depending on invalid_response_action setting (#6780)
  core: bump psycopg from 3.1.11 to 3.1.12 (#6997)
  core: bump pydantic from 2.4.0 to 2.4.1 (#6998)
  web: bump the sentry group in /web with 2 updates (#6999)
  web: bump pyright from 1.1.328 to 1.1.329 in /web (#7000)
  website/blog: improved sentence (#6995)
  website/blog: fix missing link in m2m post (#6994)
  web/user: fix incorrect link to admin interface (#6993)
  ...
This commit is contained in:
Ken Sternberg 2023-09-29 16:10:05 -07:00
commit ad1b8dcd20
59 changed files with 2938 additions and 1758 deletions

View File

@ -39,6 +39,8 @@ jobs:
- uses: actions/setup-go@v4 - uses: actions/setup-go@v4
with: with:
go-version-file: "go.mod" go-version-file: "go.mod"
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Generate API - name: Generate API
run: make gen-client-go run: make gen-client-go
- name: Go unittests - name: Go unittests

View File

@ -9,6 +9,8 @@ lifecycle/ @goauthentik/backend
schemas/ @goauthentik/backend schemas/ @goauthentik/backend
scripts/ @goauthentik/backend scripts/ @goauthentik/backend
tests/ @goauthentik/backend tests/ @goauthentik/backend
pyproject.toml @goauthentik/backend
poetry.lock @goauthentik/backend
# Infrastructure # Infrastructure
.github/ @goauthentik/infrastructure .github/ @goauthentik/infrastructure
Dockerfile @goauthentik/infrastructure Dockerfile @goauthentik/infrastructure

View File

@ -146,10 +146,10 @@ USER 1000
ENV TMPDIR=/dev/shm/ \ ENV TMPDIR=/dev/shm/ \
PYTHONDONTWRITEBYTECODE=1 \ PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \ PYTHONUNBUFFERED=1 \
PATH="/ak-root/venv/bin:$PATH" \ PATH="/ak-root/venv/bin:/lifecycle:$PATH" \
VENV_PATH="/ak-root/venv" \ VENV_PATH="/ak-root/venv" \
POETRY_VIRTUALENVS_CREATE=false POETRY_VIRTUALENVS_CREATE=false
HEALTHCHECK --interval=30s --timeout=30s --start-period=60s --retries=3 CMD [ "/lifecycle/ak", "healthcheck" ] HEALTHCHECK --interval=30s --timeout=30s --start-period=60s --retries=3 CMD [ "ak", "healthcheck" ]
ENTRYPOINT [ "dumb-init", "--", "/lifecycle/ak" ] ENTRYPOINT [ "dumb-init", "--", "ak" ]

View File

@ -1,9 +1,16 @@
.SHELLFLAGS += -x -e .PHONY: gen dev-reset all clean test web website
.SHELLFLAGS += ${SHELLFLAGS} -e
PWD = $(shell pwd) PWD = $(shell pwd)
UID = $(shell id -u) UID = $(shell id -u)
GID = $(shell id -g) GID = $(shell id -g)
NPM_VERSION = $(shell python -m scripts.npm_version) NPM_VERSION = $(shell python -m scripts.npm_version)
PY_SOURCES = authentik tests scripts lifecycle PY_SOURCES = authentik tests scripts lifecycle
DOCKER_IMAGE ?= "authentik:test"
pg_user := $(shell python -m authentik.lib.config postgresql.user 2>/dev/null)
pg_host := $(shell python -m authentik.lib.config postgresql.host 2>/dev/null)
pg_name := $(shell python -m authentik.lib.config postgresql.name 2>/dev/null)
CODESPELL_ARGS = -D - -D .github/codespell-dictionary.txt \ CODESPELL_ARGS = -D - -D .github/codespell-dictionary.txt \
-I .github/codespell-words.txt \ -I .github/codespell-words.txt \
@ -19,57 +26,78 @@ CODESPELL_ARGS = -D - -D .github/codespell-dictionary.txt \
website/integrations \ website/integrations \
website/src website/src
all: lint-fix lint test gen web all: lint-fix lint test gen web ## Lint, build, and test everything
help: ## Show this help
@echo "\nSpecify a command. The choices are:\n"
@grep -E '^[0-9a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":.*?## "}; {printf " \033[0;36m%-24s\033[m %s\n", $$1, $$2}' | \
sort
@echo ""
test-go: test-go:
go test -timeout 0 -v -race -cover ./... go test -timeout 0 -v -race -cover ./...
test-docker: test-docker: ## Run all tests in a docker-compose
echo "PG_PASS=$(openssl rand -base64 32)" >> .env echo "PG_PASS=$(openssl rand -base64 32)" >> .env
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 32)" >> .env echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 32)" >> .env
docker-compose pull -q docker-compose pull -q
docker-compose up --no-start docker-compose up --no-start
docker-compose start postgresql redis docker-compose start postgresql redis
docker-compose run -u root server test docker-compose run -u root server test-all
rm -f .env rm -f .env
test: test: ## Run the server tests and produce a coverage report (locally)
coverage run manage.py test --keepdb authentik coverage run manage.py test --keepdb authentik
coverage html coverage html
coverage report coverage report
lint-fix: lint-fix: ## Lint and automatically fix errors in the python source code. Reports spelling errors.
isort authentik $(PY_SOURCES) isort authentik $(PY_SOURCES)
black authentik $(PY_SOURCES) black authentik $(PY_SOURCES)
ruff authentik $(PY_SOURCES) ruff authentik $(PY_SOURCES)
codespell -w $(CODESPELL_ARGS) codespell -w $(CODESPELL_ARGS)
lint: lint: ## Lint the python and golang sources
pylint $(PY_SOURCES) pylint $(PY_SOURCES)
bandit -r $(PY_SOURCES) -x node_modules bandit -r $(PY_SOURCES) -x node_modules
golangci-lint run -v golangci-lint run -v
migrate: migrate: ## Run the Authentik Django server's migrations
python -m lifecycle.migrate python -m lifecycle.migrate
i18n-extract: i18n-extract-core web-i18n-extract i18n-extract: i18n-extract-core web-i18n-extract ## Extract strings that require translation into files to send to a translation service
i18n-extract-core: i18n-extract-core:
ak makemessages --ignore web --ignore internal --ignore web --ignore web-api --ignore website -l en ak makemessages --ignore web --ignore internal --ignore web --ignore web-api --ignore website -l en
install: web-install website-install ## Install all requires dependencies for `web`, `website` and `core`
poetry install
dev-drop-db:
echo dropdb -U ${pg_user} -h ${pg_host} ${pg_name}
# Also remove the test-db if it exists
dropdb -U ${pg_user} -h ${pg_host} test_${pg_name} || true
echo redis-cli -n 0 flushall
dev-create-db:
createdb -U ${pg_user} -h ${pg_host} ${pg_name}
dev-reset: dev-drop-db dev-create-db migrate ## Drop and restore the Authentik PostgreSQL instance to a "fresh install" state.
######################### #########################
## API Schema ## API Schema
######################### #########################
gen-build: gen-build: ## Extract the schema from the database
AUTHENTIK_DEBUG=true ak make_blueprint_schema > blueprints/schema.json AUTHENTIK_DEBUG=true ak make_blueprint_schema > blueprints/schema.json
AUTHENTIK_DEBUG=true ak spectacular --file schema.yml AUTHENTIK_DEBUG=true ak spectacular --file schema.yml
gen-changelog: gen-changelog: ## (Release) generate the changelog based from the commits since the last tag
git log --pretty=format:" - %s" $(shell git describe --tags $(shell git rev-list --tags --max-count=1))...$(shell git branch --show-current) | sort > changelog.md git log --pretty=format:" - %s" $(shell git describe --tags $(shell git rev-list --tags --max-count=1))...$(shell git branch --show-current) | sort > changelog.md
npx prettier --write changelog.md npx prettier --write changelog.md
gen-diff: gen-diff: ## (Release) generate the changelog diff between the current schema and the last tag
git show $(shell git describe --tags $(shell git rev-list --tags --max-count=1)):schema.yml > old_schema.yml git show $(shell git describe --tags $(shell git rev-list --tags --max-count=1)):schema.yml > old_schema.yml
docker run \ docker run \
--rm -v ${PWD}:/local \ --rm -v ${PWD}:/local \
@ -84,7 +112,7 @@ gen-clean:
rm -rf web/api/src/ rm -rf web/api/src/
rm -rf api/ rm -rf api/
gen-client-ts: gen-client-ts: ## Build and install the authentik API for Typescript into the authentik UI Application
docker run \ docker run \
--rm -v ${PWD}:/local \ --rm -v ${PWD}:/local \
--user ${UID}:${GID} \ --user ${UID}:${GID} \
@ -100,7 +128,7 @@ gen-client-ts:
cd gen-ts-api && npm i cd gen-ts-api && npm i
\cp -rfv gen-ts-api/* web/node_modules/@goauthentik/api \cp -rfv gen-ts-api/* web/node_modules/@goauthentik/api
gen-client-go: gen-client-go: ## Build and install the authentik API for Golang
mkdir -p ./gen-go-api ./gen-go-api/templates mkdir -p ./gen-go-api ./gen-go-api/templates
wget https://raw.githubusercontent.com/goauthentik/client-go/main/config.yaml -O ./gen-go-api/config.yaml wget https://raw.githubusercontent.com/goauthentik/client-go/main/config.yaml -O ./gen-go-api/config.yaml
wget https://raw.githubusercontent.com/goauthentik/client-go/main/templates/README.mustache -O ./gen-go-api/templates/README.mustache wget https://raw.githubusercontent.com/goauthentik/client-go/main/templates/README.mustache -O ./gen-go-api/templates/README.mustache
@ -117,7 +145,7 @@ gen-client-go:
go mod edit -replace goauthentik.io/api/v3=./gen-go-api go mod edit -replace goauthentik.io/api/v3=./gen-go-api
rm -rf ./gen-go-api/config.yaml ./gen-go-api/templates/ rm -rf ./gen-go-api/config.yaml ./gen-go-api/templates/
gen-dev-config: gen-dev-config: ## Generate a local development config file
python -m scripts.generate_config python -m scripts.generate_config
gen: gen-build gen-clean gen-client-ts gen: gen-build gen-clean gen-client-ts
@ -126,21 +154,21 @@ gen: gen-build gen-clean gen-client-ts
## Web ## Web
######################### #########################
web-build: web-install web-build: web-install ## Build the Authentik UI
cd web && npm run build cd web && npm run build
web: web-lint-fix web-lint web-check-compile web: web-lint-fix web-lint web-check-compile web-i18n-extract ## Automatically fix formatting issues in the Authentik UI source code, lint the code, and compile it
web-install: web-install: ## Install the necessary libraries to build the Authentik UI
cd web && npm ci cd web && npm ci
web-watch: web-watch: ## Build and watch the Authentik UI for changes, updating automatically
rm -rf web/dist/ rm -rf web/dist/
mkdir web/dist/ mkdir web/dist/
touch web/dist/.gitkeep touch web/dist/.gitkeep
cd web && npm run watch cd web && npm run watch
web-storybook-watch: web-storybook-watch: ## Build and run the storybook documentation server
cd web && npm run storybook cd web && npm run storybook
web-lint-fix: web-lint-fix:
@ -160,7 +188,7 @@ web-i18n-extract:
## Website ## Website
######################### #########################
website: website-lint-fix website-build website: website-lint-fix website-build ## Automatically fix formatting issues in the Authentik website/docs source code, lint the code, and compile it
website-install: website-install:
cd website && npm ci cd website && npm ci
@ -171,15 +199,15 @@ website-lint-fix:
website-build: website-build:
cd website && npm run build cd website && npm run build
website-watch: website-watch: ## Build and watch the documentation website, updating automatically
cd website && npm run watch cd website && npm run watch
######################### #########################
## Docker ## Docker
######################### #########################
docker: docker: ## Build a docker image of the current source tree
DOCKER_BUILDKIT=1 docker build . --progress plain --tag authentik:test DOCKER_BUILDKIT=1 docker build . --progress plain --tag ${DOCKER_IMAGE}
######################### #########################
## CI ## CI
@ -214,14 +242,3 @@ ci-pyright: ci--meta-debug
ci-pending-migrations: ci--meta-debug ci-pending-migrations: ci--meta-debug
ak makemigrations --check ak makemigrations --check
install: web-install website-install
poetry install
dev-reset:
dropdb -U postgres -h localhost authentik
# Also remove the test-db if it exists
dropdb -U postgres -h localhost test_authentik || true
createdb -U postgres -h localhost authentik
redis-cli -n 0 flushall
make migrate

View File

@ -41,15 +41,3 @@ See [SECURITY.md](SECURITY.md)
## Adoption and Contributions ## Adoption and Contributions
Your organization uses authentik? We'd love to add your logo to the readme and our website! Email us @ hello@goauthentik.io or open a GitHub Issue/PR! For more information on how to contribute to authentik, please refer to our [CONTRIBUTING.md file](./CONTRIBUTING.md). Your organization uses authentik? We'd love to add your logo to the readme and our website! Email us @ hello@goauthentik.io or open a GitHub Issue/PR! For more information on how to contribute to authentik, please refer to our [CONTRIBUTING.md file](./CONTRIBUTING.md).
## Sponsors
This project is proudly sponsored by:
<p>
<a href="https://www.digitalocean.com/?utm_medium=opensource&utm_source=goauthentik.io">
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
</a>
</p>
DigitalOcean provides development and testing resources for authentik.

View File

@ -616,8 +616,10 @@ class UserViewSet(UsedByMixin, ModelViewSet):
if not request.user.has_perm("impersonate"): if not request.user.has_perm("impersonate"):
LOGGER.debug("User attempted to impersonate without permissions", user=request.user) LOGGER.debug("User attempted to impersonate without permissions", user=request.user)
return Response(status=401) return Response(status=401)
user_to_be = self.get_object() user_to_be = self.get_object()
if user_to_be.pk == self.request.user.pk:
LOGGER.debug("User attempted to impersonate themselves", user=request.user)
return Response(status=401)
request.session[SESSION_KEY_IMPERSONATE_ORIGINAL_USER] = request.user request.session[SESSION_KEY_IMPERSONATE_ORIGINAL_USER] = request.user
request.session[SESSION_KEY_IMPERSONATE_USER] = user_to_be request.session[SESSION_KEY_IMPERSONATE_USER] = user_to_be

View File

@ -29,7 +29,7 @@ class Command(BaseCommand):
no_color=False, no_color=False,
quiet=True, quiet=True,
optimization="fair", optimization="fair",
autoscale=(3, 1), autoscale=(CONFIG.get_int("worker.concurrency"), 1),
task_events=True, task_events=True,
beat=options.get("beat", True), beat=options.get("beat", True),
schedule_filename=f"{tempdir}/celerybeat-schedule", schedule_filename=f"{tempdir}/celerybeat-schedule",

View File

@ -6,6 +6,7 @@ from rest_framework.test import APITestCase
from authentik.core.models import User from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user from authentik.core.tests.utils import create_test_admin_user
from authentik.lib.config import CONFIG
class TestImpersonation(APITestCase): class TestImpersonation(APITestCase):
@ -46,12 +47,42 @@ class TestImpersonation(APITestCase):
"""test impersonation without permissions""" """test impersonation without permissions"""
self.client.force_login(self.other_user) self.client.force_login(self.other_user)
self.client.get(reverse("authentik_api:user-impersonate", kwargs={"pk": self.user.pk})) response = self.client.post(
reverse("authentik_api:user-impersonate", kwargs={"pk": self.user.pk})
)
self.assertEqual(response.status_code, 403)
response = self.client.get(reverse("authentik_api:user-me")) response = self.client.get(reverse("authentik_api:user-me"))
response_body = loads(response.content.decode()) response_body = loads(response.content.decode())
self.assertEqual(response_body["user"]["username"], self.other_user.username) self.assertEqual(response_body["user"]["username"], self.other_user.username)
@CONFIG.patch("impersonation", False)
def test_impersonate_disabled(self):
"""test impersonation that is disabled"""
self.client.force_login(self.user)
response = self.client.post(
reverse("authentik_api:user-impersonate", kwargs={"pk": self.other_user.pk})
)
self.assertEqual(response.status_code, 401)
response = self.client.get(reverse("authentik_api:user-me"))
response_body = loads(response.content.decode())
self.assertEqual(response_body["user"]["username"], self.user.username)
def test_impersonate_self(self):
"""test impersonation that user can't impersonate themselves"""
self.client.force_login(self.user)
response = self.client.post(
reverse("authentik_api:user-impersonate", kwargs={"pk": self.user.pk})
)
self.assertEqual(response.status_code, 401)
response = self.client.get(reverse("authentik_api:user-me"))
response_body = loads(response.content.decode())
self.assertEqual(response_body["user"]["username"], self.user.username)
def test_un_impersonate_empty(self): def test_un_impersonate_empty(self):
"""test un-impersonation without impersonating first""" """test un-impersonation without impersonating first"""
self.client.force_login(self.other_user) self.client.force_login(self.other_user)

View File

@ -42,6 +42,7 @@ from authentik.flows.models import (
FlowDesignation, FlowDesignation,
FlowStageBinding, FlowStageBinding,
FlowToken, FlowToken,
InvalidResponseAction,
Stage, Stage,
) )
from authentik.flows.planner import ( from authentik.flows.planner import (
@ -105,7 +106,7 @@ class FlowExecutorView(APIView):
flow: Flow flow: Flow
plan: Optional[FlowPlan] = None plan: Optional[FlowPlan] = None
current_binding: FlowStageBinding current_binding: Optional[FlowStageBinding] = None
current_stage: Stage current_stage: Stage
current_stage_view: View current_stage_view: View
@ -411,6 +412,19 @@ class FlowExecutorView(APIView):
Optionally, an exception can be passed, which will be shown if the current user Optionally, an exception can be passed, which will be shown if the current user
is a superuser.""" is a superuser."""
self._logger.debug("f(exec): Stage invalid") self._logger.debug("f(exec): Stage invalid")
if self.current_binding and self.current_binding.invalid_response_action in [
InvalidResponseAction.RESTART,
InvalidResponseAction.RESTART_WITH_CONTEXT,
]:
keep_context = (
self.current_binding.invalid_response_action
== InvalidResponseAction.RESTART_WITH_CONTEXT
)
self._logger.debug(
"f(exec): Invalid response, restarting flow",
keep_context=keep_context,
)
return self.restart_flow(keep_context)
self.cancel() self.cancel()
challenge_view = AccessDeniedChallengeView(self, error_message) challenge_view = AccessDeniedChallengeView(self, error_message)
challenge_view.request = self.request challenge_view.request = self.request

View File

@ -111,3 +111,6 @@ web:
# No default here as it's set dynamically # No default here as it's set dynamically
# workers: 2 # workers: 2
threads: 4 threads: 4
worker:
concurrency: 2

View File

@ -37,6 +37,7 @@ CSRF_HEADER_NAME = "HTTP_X_AUTHENTIK_CSRF"
LANGUAGE_COOKIE_NAME = "authentik_language" LANGUAGE_COOKIE_NAME = "authentik_language"
SESSION_COOKIE_NAME = "authentik_session" SESSION_COOKIE_NAME = "authentik_session"
SESSION_COOKIE_DOMAIN = CONFIG.get("cookie_domain", None) SESSION_COOKIE_DOMAIN = CONFIG.get("cookie_domain", None)
APPEND_SLASH = False
AUTHENTICATION_BACKENDS = [ AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend", "django.contrib.auth.backends.ModelBackend",
@ -332,7 +333,7 @@ LOCALE_PATHS = ["./locale"]
CELERY = { CELERY = {
"task_soft_time_limit": 600, "task_soft_time_limit": 600,
"worker_max_tasks_per_child": 50, "worker_max_tasks_per_child": 50,
"worker_concurrency": 2, "worker_concurrency": CONFIG.get_int("worker.concurrency"),
"beat_schedule": { "beat_schedule": {
"clean_expired_models": { "clean_expired_models": {
"task": "authentik.core.tasks.clean_expired_models", "task": "authentik.core.tasks.clean_expired_models",

View File

@ -133,7 +133,7 @@ class BaseLDAPSynchronizer:
def build_user_properties(self, user_dn: str, **kwargs) -> dict[str, Any]: def build_user_properties(self, user_dn: str, **kwargs) -> dict[str, Any]:
"""Build attributes for User object based on property mappings.""" """Build attributes for User object based on property mappings."""
props = self._build_object_properties(user_dn, self._source.property_mappings, **kwargs) props = self._build_object_properties(user_dn, self._source.property_mappings, **kwargs)
props["path"] = self._source.get_user_path() props.setdefault("path", self._source.get_user_path())
return props return props
def build_group_properties(self, group_dn: str, **kwargs) -> dict[str, Any]: def build_group_properties(self, group_dn: str, **kwargs) -> dict[str, Any]:
@ -151,7 +151,9 @@ class BaseLDAPSynchronizer:
continue continue
mapping: LDAPPropertyMapping mapping: LDAPPropertyMapping
try: try:
value = mapping.evaluate(user=None, request=None, ldap=kwargs, dn=object_dn) value = mapping.evaluate(
user=None, request=None, ldap=kwargs, dn=object_dn, source=self._source
)
if value is None: if value is None:
self._logger.warning("property mapping returned None", mapping=mapping) self._logger.warning("property mapping returned None", mapping=mapping)
continue continue

View File

@ -55,7 +55,7 @@ def mock_ad_connection(password: str) -> Connection:
"revision": 0, "revision": 0,
"objectSid": "user0", "objectSid": "user0",
"objectClass": "person", "objectClass": "person",
"distinguishedName": "cn=user0,ou=users,dc=goauthentik,dc=io", "distinguishedName": "cn=user0,ou=foo,ou=users,dc=goauthentik,dc=io",
"userAccountControl": ( "userAccountControl": (
UserAccountControl.ACCOUNTDISABLE + UserAccountControl.NORMAL_ACCOUNT UserAccountControl.ACCOUNTDISABLE + UserAccountControl.NORMAL_ACCOUNT
), ),

View File

@ -123,6 +123,7 @@ class LDAPSyncTests(TestCase):
user = User.objects.filter(username="user0_sn").first() user = User.objects.filter(username="user0_sn").first()
self.assertEqual(user.attributes["foo"], "bar") self.assertEqual(user.attributes["foo"], "bar")
self.assertFalse(user.is_active) self.assertFalse(user.is_active)
self.assertEqual(user.path, "goauthentik.io/sources/ldap/users/foo")
self.assertFalse(User.objects.filter(username="user1_sn").exists()) self.assertFalse(User.objects.filter(username="user1_sn").exists())
def test_sync_users_openldap(self): def test_sync_users_openldap(self):

View File

@ -4,6 +4,27 @@ metadata:
blueprints.goauthentik.io/system: "true" blueprints.goauthentik.io/system: "true"
name: System - LDAP Source - Mappings name: System - LDAP Source - Mappings
entries: entries:
- identifiers:
managed: goauthentik.io/sources/ldap/default-dn-path
model: authentik_sources_ldap.ldappropertymapping
attrs:
name: "authentik default LDAP Mapping: DN to User Path"
object_field: "path"
expression: |
dn = ldap.get("distinguishedName")
path_elements = []
for pair in dn.split(","):
attr, _, value = pair.partition("=")
# Ignore elements from the Root DSE and the canonical name of the object
if attr.lower() in ["cn", "dc"]:
continue
path_elements.append(value)
path_elements.reverse()
path = source.get_user_path()
if len(path_elements) > 0:
path = f"{path}/{'/'.join(path_elements)}"
return path
- identifiers: - identifiers:
managed: goauthentik.io/sources/ldap/default-name managed: goauthentik.io/sources/ldap/default-name
model: authentik_sources_ldap.ldappropertymapping model: authentik_sources_ldap.ldappropertymapping

View File

@ -9,6 +9,7 @@ import (
"github.com/getsentry/sentry-go" "github.com/getsentry/sentry-go"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"goauthentik.io/internal/common" "goauthentik.io/internal/common"
"goauthentik.io/internal/config" "goauthentik.io/internal/config"
"goauthentik.io/internal/constants" "goauthentik.io/internal/constants"

13
go.mod
View File

@ -6,7 +6,6 @@ require (
beryju.io/ldap v0.1.0 beryju.io/ldap v0.1.0
github.com/Netflix/go-env v0.0.0-20210215222557-e437a7e7f9fb github.com/Netflix/go-env v0.0.0-20210215222557-e437a7e7f9fb
github.com/coreos/go-oidc v2.2.1+incompatible github.com/coreos/go-oidc v2.2.1+incompatible
github.com/garyburd/redigo v1.6.4
github.com/getsentry/sentry-go v0.24.1 github.com/getsentry/sentry-go v0.24.1
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1 github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
github.com/go-ldap/ldap/v3 v3.4.6 github.com/go-ldap/ldap/v3 v3.4.6
@ -22,7 +21,8 @@ require (
github.com/jellydator/ttlcache/v3 v3.1.0 github.com/jellydator/ttlcache/v3 v3.1.0
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
github.com/pires/go-proxyproto v0.7.0 github.com/pires/go-proxyproto v0.7.0
github.com/prometheus/client_golang v1.16.0 github.com/prometheus/client_golang v1.17.0
github.com/redis/go-redis/v9 v9.2.1
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.7.0 github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
@ -30,7 +30,6 @@ require (
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.12.0 golang.org/x/oauth2 v0.12.0
golang.org/x/sync v0.3.0 golang.org/x/sync v0.3.0
gopkg.in/boj/redistore.v1 v1.0.0-20160128113310-fc113767cd6b
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
layeh.com/radius v0.0.0-20210819152912-ad72663a72ab layeh.com/radius v0.0.0-20210819152912-ad72663a72ab
) )
@ -41,6 +40,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/felixge/httpsnoop v1.0.1 // indirect github.com/felixge/httpsnoop v1.0.1 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
github.com/go-http-utils/fresh v0.0.0-20161124030543-7231e26a4b27 // indirect github.com/go-http-utils/fresh v0.0.0-20161124030543-7231e26a4b27 // indirect
@ -65,10 +65,9 @@ require (
github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac // indirect github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac // indirect
github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect github.com/prometheus/procfs v0.11.1 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
go.mongodb.org/mongo-driver v1.11.3 // indirect go.mongodb.org/mongo-driver v1.11.3 // indirect
go.opentelemetry.io/otel v1.14.0 // indirect go.opentelemetry.io/otel v1.14.0 // indirect

27
go.sum
View File

@ -48,6 +48,8 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@ -63,14 +65,14 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/garyburd/redigo v1.6.4 h1:LFu2R3+ZOPgSMWMOL+saa/zXRjw0ID2G8FepO53BGlg=
github.com/garyburd/redigo v1.6.4/go.mod h1:rTb6epsqigu3kYKBnaF028A7Tf/Aw5s0cqA47doKKqw=
github.com/getsentry/sentry-go v0.24.1 h1:W6/0GyTy8J6ge6lVCc94WB6Gx2ZuLrgopnn9w8Hiwuk= github.com/getsentry/sentry-go v0.24.1 h1:W6/0GyTy8J6ge6lVCc94WB6Gx2ZuLrgopnn9w8Hiwuk=
github.com/getsentry/sentry-go v0.24.1/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/getsentry/sentry-go v0.24.1/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
@ -277,20 +279,21 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac h1:jWKYCNlX4J5s8M0nHYkh7Y7c9gRVDEb3mq51j5J0F5M= github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac h1:jWKYCNlX4J5s8M0nHYkh7Y7c9gRVDEb3mq51j5J0F5M=
github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac/go.mod h1:hoLfEwdY11HjRfKFH6KqnPsfxlo3BP6bJehpDv8t6sQ= github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac/go.mod h1:hoLfEwdY11HjRfKFH6KqnPsfxlo3BP6bJehpDv8t6sQ=
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg=
github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
@ -638,8 +641,6 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/boj/redistore.v1 v1.0.0-20160128113310-fc113767cd6b h1:U/Uqd1232+wrnHOvWNaxrNqn/kFnr4yu4blgPtQt0N8=
gopkg.in/boj/redistore.v1 v1.0.0-20160128113310-fc113767cd6b/go.mod h1:fgfIZMlsafAHpspcks2Bul+MWUNw/2dyQmjC2faKjtg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -1,14 +1,20 @@
package gounicorn package gounicorn
import ( import (
"fmt"
"os" "os"
"os/exec" "os/exec"
"os/signal"
"runtime" "runtime"
"strconv"
"strings"
"syscall" "syscall"
"time" "time"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"goauthentik.io/internal/config" "goauthentik.io/internal/config"
"goauthentik.io/internal/utils"
) )
type GoUnicorn struct { type GoUnicorn struct {
@ -17,6 +23,7 @@ type GoUnicorn struct {
log *log.Entry log *log.Entry
p *exec.Cmd p *exec.Cmd
pidFile string
started bool started bool
killed bool killed bool
alive bool alive bool
@ -33,15 +40,36 @@ func New(healthcheck func() bool) *GoUnicorn {
HealthyCallback: func() {}, HealthyCallback: func() {},
} }
g.initCmd() g.initCmd()
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGUSR2)
go func() {
for sig := range c {
if sig == syscall.SIGHUP {
g.log.Info("SIGHUP received, forwarding to gunicorn")
g.Reload()
} else if sig == syscall.SIGUSR2 {
g.log.Info("SIGUSR2 received, restarting gunicorn")
g.Restart()
}
}
}()
return g return g
} }
func (g *GoUnicorn) initCmd() { func (g *GoUnicorn) initCmd() {
command := "gunicorn" command := "./manage.py"
args := []string{"-c", "./lifecycle/gunicorn.conf.py", "authentik.root.asgi:application"} args := []string{"dev_server"}
if config.Get().Debug { if !config.Get().Debug {
command = "./manage.py" pidFile, err := os.CreateTemp("", "authentik-gunicorn.*.pid")
args = []string{"dev_server"} if err != nil {
panic(fmt.Errorf("failed to create temporary pid file: %v", err))
}
g.pidFile = pidFile.Name()
command = "gunicorn"
args = []string{"-c", "./lifecycle/gunicorn.conf.py", "authentik.root.asgi:application"}
if g.pidFile != "" {
args = append(args, "--pid", g.pidFile)
}
} }
g.log.WithField("args", args).WithField("cmd", command).Debug("Starting gunicorn") g.log.WithField("args", args).WithField("cmd", command).Debug("Starting gunicorn")
g.p = exec.Command(command, args...) g.p = exec.Command(command, args...)
@ -55,13 +83,10 @@ func (g *GoUnicorn) IsRunning() bool {
} }
func (g *GoUnicorn) Start() error { func (g *GoUnicorn) Start() error {
if g.killed {
g.log.Debug("Not restarting gunicorn since we're shutdown")
return nil
}
if g.started { if g.started {
g.initCmd() g.initCmd()
} }
g.killed = false
g.started = true g.started = true
go g.healthcheck() go g.healthcheck()
return g.p.Run() return g.p.Run()
@ -85,8 +110,76 @@ func (g *GoUnicorn) healthcheck() {
} }
} }
func (g *GoUnicorn) Reload() {
g.log.WithField("method", "reload").Info("reloading gunicorn")
err := g.p.Process.Signal(syscall.SIGHUP)
if err != nil {
g.log.WithError(err).Warning("failed to reload gunicorn")
}
}
func (g *GoUnicorn) Restart() {
g.log.WithField("method", "restart").Info("restart gunicorn")
if g.pidFile == "" {
g.log.Warning("pidfile is non existent, cannot restart")
return
}
err := g.p.Process.Signal(syscall.SIGUSR2)
if err != nil {
g.log.WithError(err).Warning("failed to restart gunicorn")
return
}
newPidFile := fmt.Sprintf("%s.2", g.pidFile)
// Wait for the new PID file to be created
for range time.NewTicker(1 * time.Second).C {
_, err = os.Stat(newPidFile)
if err == nil || !os.IsNotExist(err) {
break
}
g.log.Debugf("waiting for new gunicorn pidfile to appear at %s", newPidFile)
}
if err != nil {
g.log.WithError(err).Warning("failed to find the new gunicorn process, aborting")
return
}
newPidB, err := os.ReadFile(newPidFile)
if err != nil {
g.log.WithError(err).Warning("failed to find the new gunicorn process, aborting")
return
}
newPidS := strings.TrimSpace(string(newPidB[:]))
newPid, err := strconv.Atoi(newPidS)
if err != nil {
g.log.WithError(err).Warning("failed to find the new gunicorn process, aborting")
return
}
g.log.Warningf("new gunicorn PID is %d", newPid)
newProcess, err := utils.FindProcess(newPid)
if newProcess == nil || err != nil {
g.log.WithError(err).Warning("failed to find the new gunicorn process, aborting")
return
}
// The new process has started, let's gracefully kill the old one
g.log.Warning("killing old gunicorn")
err = g.p.Process.Signal(syscall.SIGTERM)
if err != nil {
g.log.Warning("failed to kill old instance of gunicorn")
}
g.p.Process = newProcess
// No need to close any files and the .2 pid file is deleted by Gunicorn
}
func (g *GoUnicorn) Kill() { func (g *GoUnicorn) Kill() {
g.killed = true if !g.started {
return
}
var err error var err error
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
g.log.WithField("method", "kill").Warning("stopping gunicorn") g.log.WithField("method", "kill").Warning("stopping gunicorn")
@ -98,4 +191,11 @@ func (g *GoUnicorn) Kill() {
if err != nil { if err != nil {
g.log.WithError(err).Warning("failed to stop gunicorn") g.log.WithError(err).Warning("failed to stop gunicorn")
} }
if g.pidFile != "" {
err := os.Remove(g.pidFile)
if err != nil {
g.log.WithError(err).Warning("failed to remove pidfile")
}
}
g.killed = true
} }

View File

@ -280,7 +280,7 @@ func (a *Application) handleSignOut(rw http.ResponseWriter, r *http.Request) {
"id_token_hint": []string{cc.RawToken}, "id_token_hint": []string{cc.RawToken},
} }
redirect += "?" + uv.Encode() redirect += "?" + uv.Encode()
err = a.Logout(cc.Sub) err = a.Logout(r.Context(), cc.Sub)
if err != nil { if err != nil {
a.log.WithError(err).Warning("failed to logout of other sessions") a.log.WithError(err).Warning("failed to logout of other sessions")
} }

View File

@ -1,23 +1,23 @@
package application package application
import ( import (
"context"
"fmt" "fmt"
"math" "math"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"path" "path"
"strconv"
"strings" "strings"
"github.com/garyburd/redigo/redis"
"github.com/gorilla/securecookie" "github.com/gorilla/securecookie"
"github.com/gorilla/sessions" "github.com/gorilla/sessions"
"github.com/redis/go-redis/v9"
"goauthentik.io/api/v3" "goauthentik.io/api/v3"
"goauthentik.io/internal/config" "goauthentik.io/internal/config"
"goauthentik.io/internal/outpost/proxyv2/codecs" "goauthentik.io/internal/outpost/proxyv2/codecs"
"goauthentik.io/internal/outpost/proxyv2/constants" "goauthentik.io/internal/outpost/proxyv2/constants"
"gopkg.in/boj/redistore.v1" "goauthentik.io/internal/outpost/proxyv2/redisstore"
) )
const RedisKeyPrefix = "authentik_proxy_session_" const RedisKeyPrefix = "authentik_proxy_session_"
@ -30,20 +30,28 @@ func (a *Application) getStore(p api.ProxyOutpostConfig, externalHost *url.URL)
maxAge = int(*t) + 1 maxAge = int(*t) + 1
} }
if a.isEmbedded { if a.isEmbedded {
rs, err := redistore.NewRediStoreWithDB(10, "tcp", fmt.Sprintf("%s:%d", config.Get().Redis.Host, config.Get().Redis.Port), config.Get().Redis.Password, strconv.Itoa(config.Get().Redis.DB)) client := redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%d", config.Get().Redis.Host, config.Get().Redis.Port),
// Username: config.Get().Redis.Password,
Password: config.Get().Redis.Password,
DB: config.Get().Redis.DB,
})
// New default RedisStore
rs, err := redisstore.NewRedisStore(context.Background(), client)
if err != nil { if err != nil {
panic(err) panic(err)
} }
rs.Codecs = codecs.CodecsFromPairs(maxAge, []byte(*p.CookieSecret))
rs.SetMaxLength(math.MaxInt)
rs.SetKeyPrefix(RedisKeyPrefix)
rs.Options.HttpOnly = true rs.KeyPrefix(RedisKeyPrefix)
if strings.ToLower(externalHost.Scheme) == "https" { rs.Options(sessions.Options{
rs.Options.Secure = true HttpOnly: true,
} Secure: strings.ToLower(externalHost.Scheme) == "https",
rs.Options.Domain = *p.CookieDomain Domain: *p.CookieDomain,
rs.Options.SameSite = http.SameSiteLaxMode SameSite: http.SameSiteLaxMode,
MaxAge: maxAge,
})
a.log.Trace("using redis session backend") a.log.Trace("using redis session backend")
return rs return rs
} }
@ -80,7 +88,7 @@ func (a *Application) getAllCodecs() []securecookie.Codec {
return cs return cs
} }
func (a *Application) Logout(sub string) error { func (a *Application) Logout(ctx context.Context, sub string) error {
if _, ok := a.sessions.(*sessions.FilesystemStore); ok { if _, ok := a.sessions.(*sessions.FilesystemStore); ok {
files, err := os.ReadDir(os.TempDir()) files, err := os.ReadDir(os.TempDir())
if err != nil { if err != nil {
@ -120,31 +128,22 @@ func (a *Application) Logout(sub string) error {
} }
} }
} }
if rs, ok := a.sessions.(*redistore.RediStore); ok { if rs, ok := a.sessions.(*redisstore.RedisStore); ok {
pool := rs.Pool.Get() client := rs.Client()
defer pool.Close() defer client.Close()
rep, err := pool.Do("KEYS", fmt.Sprintf("%s*", RedisKeyPrefix)) keys, err := client.Keys(ctx, fmt.Sprintf("%s*", RedisKeyPrefix)).Result()
if err != nil { if err != nil {
return err return err
} }
keys, err := redis.Strings(rep, err) serializer := redisstore.GobSerializer{}
if err != nil {
return err
}
serializer := redistore.GobSerializer{}
for _, key := range keys { for _, key := range keys {
v, err := pool.Do("GET", key) v, err := client.Get(ctx, key).Result()
if err != nil { if err != nil {
a.log.WithError(err).Warning("failed to get value") a.log.WithError(err).Warning("failed to get value")
continue continue
} }
b, err := redis.Bytes(v, err)
if err != nil {
a.log.WithError(err).Warning("failed to load value")
continue
}
s := sessions.Session{} s := sessions.Session{}
err = serializer.Deserialize(b, &s) err = serializer.Deserialize([]byte(v), &s)
if err != nil { if err != nil {
a.log.WithError(err).Warning("failed to deserialize") a.log.WithError(err).Warning("failed to deserialize")
continue continue
@ -156,7 +155,7 @@ func (a *Application) Logout(sub string) error {
claims := c.(Claims) claims := c.(Claims)
if claims.Sub == sub { if claims.Sub == sub {
a.log.WithField("key", key).Trace("deleting session") a.log.WithField("key", key).Trace("deleting session")
_, err := pool.Do("DEL", key) _, err := client.Del(ctx, key).Result()
if err != nil { if err != nil {
a.log.WithError(err).Warning("failed to delete key") a.log.WithError(err).Warning("failed to delete key")
continue continue

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Ruben Cervilla
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,200 @@
package redisstore
import (
"bytes"
"context"
"crypto/rand"
"encoding/base32"
"encoding/gob"
"errors"
"io"
"net/http"
"strings"
"time"
"github.com/gorilla/sessions"
"github.com/redis/go-redis/v9"
)
// RedisStore stores gorilla sessions in Redis
type RedisStore struct {
// client to connect to redis
client redis.UniversalClient
// default options to use when a new session is created
options sessions.Options
// key prefix with which the session will be stored
keyPrefix string
// key generator
keyGen KeyGenFunc
// session serializer
serializer SessionSerializer
}
// KeyGenFunc defines a function used by store to generate a key
type KeyGenFunc func() (string, error)
// NewRedisStore returns a new RedisStore with default configuration
func NewRedisStore(ctx context.Context, client redis.UniversalClient) (*RedisStore, error) {
rs := &RedisStore{
options: sessions.Options{
Path: "/",
MaxAge: 86400 * 30,
},
client: client,
keyPrefix: "session:",
keyGen: generateRandomKey,
serializer: GobSerializer{},
}
return rs, rs.client.Ping(ctx).Err()
}
func (s *RedisStore) Client() redis.UniversalClient {
return s.client
}
// Get returns a session for the given name after adding it to the registry.
func (s *RedisStore) Get(r *http.Request, name string) (*sessions.Session, error) {
return sessions.GetRegistry(r).Get(s, name)
}
// New returns a session for the given name without adding it to the registry.
func (s *RedisStore) New(r *http.Request, name string) (*sessions.Session, error) {
session := sessions.NewSession(s, name)
opts := s.options
session.Options = &opts
session.IsNew = true
c, err := r.Cookie(name)
if err != nil {
return session, nil
}
session.ID = c.Value
err = s.load(r.Context(), session)
if err == nil {
session.IsNew = false
} else if err == redis.Nil {
err = nil // no data stored
}
return session, err
}
// Save adds a single session to the response.
//
// If the Options.MaxAge of the session is <= 0 then the session file will be
// deleted from the store. With this process it enforces the properly
// session cookie handling so no need to trust in the cookie management in the
// web browser.
func (s *RedisStore) Save(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
// Delete if max-age is <= 0
if session.Options.MaxAge <= 0 {
if err := s.delete(r.Context(), session); err != nil {
return err
}
http.SetCookie(w, sessions.NewCookie(session.Name(), "", session.Options))
return nil
}
if session.ID == "" {
id, err := s.keyGen()
if err != nil {
return errors.New("redisstore: failed to generate session id")
}
session.ID = id
}
if err := s.save(r.Context(), session); err != nil {
return err
}
http.SetCookie(w, sessions.NewCookie(session.Name(), session.ID, session.Options))
return nil
}
// Options set options to use when a new session is created
func (s *RedisStore) Options(opts sessions.Options) {
s.options = opts
}
// KeyPrefix sets the key prefix to store session in Redis
func (s *RedisStore) KeyPrefix(keyPrefix string) {
s.keyPrefix = keyPrefix
}
// KeyGen sets the key generator function
func (s *RedisStore) KeyGen(f KeyGenFunc) {
s.keyGen = f
}
// Serializer sets the session serializer to store session
func (s *RedisStore) Serializer(ss SessionSerializer) {
s.serializer = ss
}
// Close closes the Redis store
func (s *RedisStore) Close() error {
return s.client.Close()
}
// save writes session in Redis
func (s *RedisStore) save(ctx context.Context, session *sessions.Session) error {
b, err := s.serializer.Serialize(session)
if err != nil {
return err
}
return s.client.Set(ctx, s.keyPrefix+session.ID, b, time.Duration(session.Options.MaxAge)*time.Second).Err()
}
// load reads session from Redis
func (s *RedisStore) load(ctx context.Context, session *sessions.Session) error {
cmd := s.client.Get(ctx, s.keyPrefix+session.ID)
if cmd.Err() != nil {
return cmd.Err()
}
b, err := cmd.Bytes()
if err != nil {
return err
}
return s.serializer.Deserialize(b, session)
}
// delete deletes session in Redis
func (s *RedisStore) delete(ctx context.Context, session *sessions.Session) error {
return s.client.Del(ctx, s.keyPrefix+session.ID).Err()
}
// SessionSerializer provides an interface for serialize/deserialize a session
type SessionSerializer interface {
Serialize(s *sessions.Session) ([]byte, error)
Deserialize(b []byte, s *sessions.Session) error
}
// Gob serializer
type GobSerializer struct{}
func (gs GobSerializer) Serialize(s *sessions.Session) ([]byte, error) {
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
err := enc.Encode(s.Values)
if err == nil {
return buf.Bytes(), nil
}
return nil, err
}
func (gs GobSerializer) Deserialize(d []byte, s *sessions.Session) error {
dec := gob.NewDecoder(bytes.NewBuffer(d))
return dec.Decode(&s.Values)
}
// generateRandomKey returns a new random key
func generateRandomKey() (string, error) {
k := make([]byte, 64)
if _, err := io.ReadFull(rand.Reader, k); err != nil {
return "", err
}
return strings.TrimRight(base32.StdEncoding.EncodeToString(k), "="), nil
}

View File

@ -0,0 +1,158 @@
package redisstore
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"github.com/gorilla/sessions"
"github.com/redis/go-redis/v9"
)
const (
redisAddr = "localhost:6379"
)
func TestNew(t *testing.T) {
client := redis.NewClient(&redis.Options{
Addr: redisAddr,
})
store, err := NewRedisStore(context.Background(), client)
if err != nil {
t.Fatal("failed to create redis store", err)
}
req, err := http.NewRequest("GET", "http://www.example.com", nil)
if err != nil {
t.Fatal("failed to create request", err)
}
session, err := store.New(req, "hello")
if err != nil {
t.Fatal("failed to create session", err)
}
if session.IsNew == false {
t.Fatal("session is not new")
}
}
func TestOptions(t *testing.T) {
client := redis.NewClient(&redis.Options{
Addr: redisAddr,
})
store, err := NewRedisStore(context.Background(), client)
if err != nil {
t.Fatal("failed to create redis store", err)
}
opts := sessions.Options{
Path: "/path",
MaxAge: 99999,
}
store.Options(opts)
req, err := http.NewRequest("GET", "http://www.example.com", nil)
if err != nil {
t.Fatal("failed to create request", err)
}
session, err := store.New(req, "hello")
if err != nil {
t.Fatal("failed to create store", err)
}
if session.Options.Path != opts.Path || session.Options.MaxAge != opts.MaxAge {
t.Fatal("failed to set options")
}
}
func TestSave(t *testing.T) {
client := redis.NewClient(&redis.Options{
Addr: redisAddr,
})
store, err := NewRedisStore(context.Background(), client)
if err != nil {
t.Fatal("failed to create redis store", err)
}
req, err := http.NewRequest("GET", "http://www.example.com", nil)
if err != nil {
t.Fatal("failed to create request", err)
}
w := httptest.NewRecorder()
session, err := store.New(req, "hello")
if err != nil {
t.Fatal("failed to create session", err)
}
session.Values["key"] = "value"
err = session.Save(req, w)
if err != nil {
t.Fatal("failed to save: ", err)
}
}
func TestDelete(t *testing.T) {
client := redis.NewClient(&redis.Options{
Addr: redisAddr,
})
store, err := NewRedisStore(context.Background(), client)
if err != nil {
t.Fatal("failed to create redis store", err)
}
req, err := http.NewRequest("GET", "http://www.example.com", nil)
if err != nil {
t.Fatal("failed to create request", err)
}
w := httptest.NewRecorder()
session, err := store.New(req, "hello")
if err != nil {
t.Fatal("failed to create session", err)
}
session.Values["key"] = "value"
err = session.Save(req, w)
if err != nil {
t.Fatal("failed to save session: ", err)
}
session.Options.MaxAge = -1
err = session.Save(req, w)
if err != nil {
t.Fatal("failed to delete session: ", err)
}
}
func TestClose(t *testing.T) {
client := redis.NewClient(&redis.Options{
Addr: redisAddr,
})
cmd := client.Ping(context.Background())
err := cmd.Err()
if err != nil {
t.Fatal("connection is not opened")
}
store, err := NewRedisStore(context.Background(), client)
if err != nil {
t.Fatal("failed to create redis store", err)
}
err = store.Close()
if err != nil {
t.Fatal("failed to close")
}
cmd = client.Ping(context.Background())
if cmd.Err() == nil {
t.Fatal("connection is properly closed")
}
}

39
internal/utils/process.go Normal file
View File

@ -0,0 +1,39 @@
package utils
import (
"errors"
"fmt"
"os"
"syscall"
)
func FindProcess(pid int) (*os.Process, error) {
if pid <= 0 {
return nil, fmt.Errorf("invalid pid %v", pid)
}
// The error doesn't mean anything on Unix systems, let's just check manually
// that the new gunicorn master has properly started
// https://github.com/golang/go/issues/34396
proc, err := os.FindProcess(pid)
if err != nil {
return nil, err
}
err = proc.Signal(syscall.Signal(0))
if err == nil {
return proc, nil
}
if errors.Is(err, os.ErrProcessDone) {
return nil, nil
}
errno, ok := err.(syscall.Errno)
if !ok {
return nil, err
}
switch errno {
case syscall.ESRCH:
return nil, nil
case syscall.EPERM:
return proc, nil
}
return nil, err
}

View File

@ -9,6 +9,7 @@ import (
"net/url" "net/url"
"os" "os"
"path" "path"
"time"
"github.com/gorilla/handlers" "github.com/gorilla/handlers"
"github.com/gorilla/mux" "github.com/gorilla/mux"
@ -109,6 +110,21 @@ func (ws *WebServer) attemptStartBackend() {
} }
err := ws.g.Start() err := ws.g.Start()
log.WithField("logger", "authentik.router").WithError(err).Warning("gunicorn process died, restarting") log.WithField("logger", "authentik.router").WithError(err).Warning("gunicorn process died, restarting")
if err != nil {
log.WithField("logger", "authentik.router").WithError(err).Error("gunicorn failed to start, restarting")
continue
}
failedChecks := 0
for range time.NewTicker(30 * time.Second).C {
if !ws.g.IsRunning() {
log.WithField("logger", "authentik.router").Warningf("gunicorn process failed healthcheck %d times", failedChecks)
failedChecks += 1
}
if failedChecks >= 3 {
log.WithField("logger", "authentik.router").WithError(err).Error("gunicorn process failed healthcheck three times, restarting")
break
}
}
} }
} }

View File

@ -10,17 +10,17 @@
# Charles Leclerc, 2023 # Charles Leclerc, 2023
# Titouan Petit, 2023 # Titouan Petit, 2023
# Kyllian Delaye-Maillot, 2023 # Kyllian Delaye-Maillot, 2023
# Marc Schmitt, 2023
# Manuel Viens, 2023 # Manuel Viens, 2023
# Marc Schmitt, 2023
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-21 13:04+0000\n" "POT-Creation-Date: 2023-09-15 09:51+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n" "PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Manuel Viens, 2023\n" "Last-Translator: Marc Schmitt, 2023\n"
"Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n" "Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -33,11 +33,11 @@ msgstr ""
msgid "Successfully re-scheduled Task %(name)s!" msgid "Successfully re-scheduled Task %(name)s!"
msgstr "La Tâche %(name)s a bien été reprogrammée !" msgstr "La Tâche %(name)s a bien été reprogrammée !"
#: authentik/api/schema.py:21 #: authentik/api/schema.py:25
msgid "Generic API Error" msgid "Generic API Error"
msgstr "Erreur d'API Générique" msgstr "Erreur d'API Générique"
#: authentik/api/schema.py:29 #: authentik/api/schema.py:33
msgid "Validation Error" msgid "Validation Error"
msgstr "Erreur de Validation" msgstr "Erreur de Validation"
@ -97,12 +97,12 @@ msgstr "Fournisseur SAML depuis métadonnées"
msgid "Create a SAML Provider by importing its Metadata." msgid "Create a SAML Provider by importing its Metadata."
msgstr "Créer un fournisseur SAML en important ses métadonnées." msgstr "Créer un fournisseur SAML en important ses métadonnées."
#: authentik/core/api/users.py:144 #: authentik/core/api/users.py:158
msgid "No leading or trailing slashes allowed." msgid "No leading or trailing slashes allowed."
msgstr "" msgstr ""
"Les barres obliques, ou slashes, de tête ou de queue ne sont pas autorisées." "Les barres obliques, ou slashes, de tête ou de queue ne sont pas autorisées."
#: authentik/core/api/users.py:147 #: authentik/core/api/users.py:161
msgid "No empty segments in user path allowed." msgid "No empty segments in user path allowed."
msgstr "Les segments vides dans le chemin utilisateur ne sont pas autorisés." msgstr "Les segments vides dans le chemin utilisateur ne sont pas autorisés."
@ -114,19 +114,19 @@ msgstr "nom"
msgid "Users added to this group will be superusers." msgid "Users added to this group will be superusers."
msgstr "Les utilisateurs ajoutés à ce groupe seront des super-utilisateurs." msgstr "Les utilisateurs ajoutés à ce groupe seront des super-utilisateurs."
#: authentik/core/models.py:162 #: authentik/core/models.py:142
msgid "User's display name." msgid "User's display name."
msgstr "Nom d'affichage de l'utilisateur" msgstr "Nom d'affichage de l'utilisateur"
#: authentik/core/models.py:256 authentik/providers/oauth2/models.py:294 #: authentik/core/models.py:268 authentik/providers/oauth2/models.py:295
msgid "User" msgid "User"
msgstr "Utilisateur" msgstr "Utilisateur"
#: authentik/core/models.py:257 #: authentik/core/models.py:269
msgid "Users" msgid "Users"
msgstr "Utilisateurs" msgstr "Utilisateurs"
#: authentik/core/models.py:270 #: authentik/core/models.py:282
msgid "" msgid ""
"Flow used for authentication when the associated application is accessed by " "Flow used for authentication when the associated application is accessed by "
"an un-authenticated user." "an un-authenticated user."
@ -134,11 +134,11 @@ msgstr ""
"Flux utilisé lors d'authentification quand l'application associée est " "Flux utilisé lors d'authentification quand l'application associée est "
"accédée par un utilisateur non-authentifié." "accédée par un utilisateur non-authentifié."
#: authentik/core/models.py:280 #: authentik/core/models.py:292
msgid "Flow used when authorizing this provider." msgid "Flow used when authorizing this provider."
msgstr "Flux utilisé lors de l'autorisation de ce fournisseur." msgstr "Flux utilisé lors de l'autorisation de ce fournisseur."
#: authentik/core/models.py:292 #: authentik/core/models.py:304
msgid "" msgid ""
"Accessed from applications; optional backchannel providers for protocols " "Accessed from applications; optional backchannel providers for protocols "
"like LDAP and SCIM." "like LDAP and SCIM."
@ -146,32 +146,32 @@ msgstr ""
"Accès à partir d'applications ; fournisseurs optionnels de canaux de retour " "Accès à partir d'applications ; fournisseurs optionnels de canaux de retour "
"pour des protocoles tels que LDAP et SCIM." "pour des protocoles tels que LDAP et SCIM."
#: authentik/core/models.py:347 #: authentik/core/models.py:359
msgid "Application's display Name." msgid "Application's display Name."
msgstr "Nom d'affichage de l'application" msgstr "Nom d'affichage de l'application"
#: authentik/core/models.py:348 #: authentik/core/models.py:360
msgid "Internal application name, used in URLs." msgid "Internal application name, used in URLs."
msgstr "Nom de l'application interne, utilisé dans les URLs." msgstr "Nom de l'application interne, utilisé dans les URLs."
#: authentik/core/models.py:360 #: authentik/core/models.py:372
msgid "Open launch URL in a new browser tab or window." msgid "Open launch URL in a new browser tab or window."
msgstr "" msgstr ""
"Ouvrir l'URL de lancement dans une nouvelle fenêtre ou un nouvel onglet." "Ouvrir l'URL de lancement dans une nouvelle fenêtre ou un nouvel onglet."
#: authentik/core/models.py:424 #: authentik/core/models.py:436
msgid "Application" msgid "Application"
msgstr "Application" msgstr "Application"
#: authentik/core/models.py:425 #: authentik/core/models.py:437
msgid "Applications" msgid "Applications"
msgstr "Applications" msgstr "Applications"
#: authentik/core/models.py:431 #: authentik/core/models.py:443
msgid "Use the source-specific identifier" msgid "Use the source-specific identifier"
msgstr "Utiliser l'identifiant spécifique à la source" msgstr "Utiliser l'identifiant spécifique à la source"
#: authentik/core/models.py:433 #: authentik/core/models.py:445
msgid "" msgid ""
"Link to a user with identical email address. Can have security implications " "Link to a user with identical email address. Can have security implications "
"when a source doesn't validate email addresses." "when a source doesn't validate email addresses."
@ -179,7 +179,7 @@ msgstr ""
"Lier à un utilisateur avec une adresse email identique. Peut avoir des " "Lier à un utilisateur avec une adresse email identique. Peut avoir des "
"implications de sécurité lorsqu'une source ne valide pas les adresses email." "implications de sécurité lorsqu'une source ne valide pas les adresses email."
#: authentik/core/models.py:437 #: authentik/core/models.py:449
msgid "" msgid ""
"Use the user's email address, but deny enrollment when the email address " "Use the user's email address, but deny enrollment when the email address "
"already exists." "already exists."
@ -187,7 +187,7 @@ msgstr ""
"Utiliser l'adresse courriel de l'utilisateur, mais refuser l'inscription " "Utiliser l'adresse courriel de l'utilisateur, mais refuser l'inscription "
"lorsque celle-ci existe déjà." "lorsque celle-ci existe déjà."
#: authentik/core/models.py:440 #: authentik/core/models.py:452
msgid "" msgid ""
"Link to a user with identical username. Can have security implications when " "Link to a user with identical username. Can have security implications when "
"a username is used with another source." "a username is used with another source."
@ -196,7 +196,7 @@ msgstr ""
"problèmes de sécurité si ce nom d'utilisateur est partagé avec une autre " "problèmes de sécurité si ce nom d'utilisateur est partagé avec une autre "
"source." "source."
#: authentik/core/models.py:444 #: authentik/core/models.py:456
msgid "" msgid ""
"Use the user's username, but deny enrollment when the username already " "Use the user's username, but deny enrollment when the username already "
"exists." "exists."
@ -204,23 +204,23 @@ msgstr ""
"Utiliser le nom d'utilisateur, mais refuser l'inscription si celui-ci existe" "Utiliser le nom d'utilisateur, mais refuser l'inscription si celui-ci existe"
" déjà." " déjà."
#: authentik/core/models.py:451 #: authentik/core/models.py:463
msgid "Source's display Name." msgid "Source's display Name."
msgstr "Nom d'affichage de la source." msgstr "Nom d'affichage de la source."
#: authentik/core/models.py:452 #: authentik/core/models.py:464
msgid "Internal source name, used in URLs." msgid "Internal source name, used in URLs."
msgstr "Nom interne de la source, utilisé dans les URLs." msgstr "Nom interne de la source, utilisé dans les URLs."
#: authentik/core/models.py:471 #: authentik/core/models.py:483
msgid "Flow to use when authenticating existing users." msgid "Flow to use when authenticating existing users."
msgstr "Flux à utiliser pour authentifier les utilisateurs existants." msgstr "Flux à utiliser pour authentifier les utilisateurs existants."
#: authentik/core/models.py:480 #: authentik/core/models.py:492
msgid "Flow to use when enrolling new users." msgid "Flow to use when enrolling new users."
msgstr "Flux à utiliser pour inscrire les nouveaux utilisateurs." msgstr "Flux à utiliser pour inscrire les nouveaux utilisateurs."
#: authentik/core/models.py:488 #: authentik/core/models.py:500
msgid "" msgid ""
"How the source determines if an existing user should be authenticated or a " "How the source determines if an existing user should be authenticated or a "
"new user enrolled." "new user enrolled."
@ -228,31 +228,31 @@ msgstr ""
"Comment la source détermine si un utilisateur existant doit être authentifié" "Comment la source détermine si un utilisateur existant doit être authentifié"
" ou un nouvelle utilisateur doit être inscrit." " ou un nouvelle utilisateur doit être inscrit."
#: authentik/core/models.py:660 #: authentik/core/models.py:672
msgid "Token" msgid "Token"
msgstr "Jeton" msgstr "Jeton"
#: authentik/core/models.py:661 #: authentik/core/models.py:673
msgid "Tokens" msgid "Tokens"
msgstr "Jetons" msgstr "Jetons"
#: authentik/core/models.py:702 #: authentik/core/models.py:714
msgid "Property Mapping" msgid "Property Mapping"
msgstr "Mappage de propriété" msgstr "Mappage de propriété"
#: authentik/core/models.py:703 #: authentik/core/models.py:715
msgid "Property Mappings" msgid "Property Mappings"
msgstr "Mappages de propriété" msgstr "Mappages de propriété"
#: authentik/core/models.py:738 #: authentik/core/models.py:750
msgid "Authenticated Session" msgid "Authenticated Session"
msgstr "Session Authentifiée" msgstr "Session Authentifiée"
#: authentik/core/models.py:739 #: authentik/core/models.py:751
msgid "Authenticated Sessions" msgid "Authenticated Sessions"
msgstr "Sessions Authentifiées" msgstr "Sessions Authentifiées"
#: authentik/core/sources/flow_manager.py:193 #: authentik/core/sources/flow_manager.py:189
#, python-format #, python-format
msgid "" msgid ""
"Request to authenticate with %(source)s has been denied. Please authenticate" "Request to authenticate with %(source)s has been denied. Please authenticate"
@ -261,22 +261,22 @@ msgstr ""
"La requête d'authentification avec %(source)s a été refusée. Merci de vous " "La requête d'authentification avec %(source)s a été refusée. Merci de vous "
"authentifier avec la source utilisée précédemment." "authentifier avec la source utilisée précédemment."
#: authentik/core/sources/flow_manager.py:245 #: authentik/core/sources/flow_manager.py:241
msgid "Configured flow does not exist." msgid "Configured flow does not exist."
msgstr "Le flux configuré n'existe pas." msgstr "Le flux configuré n'existe pas."
#: authentik/core/sources/flow_manager.py:275 #: authentik/core/sources/flow_manager.py:271
#: authentik/core/sources/flow_manager.py:327 #: authentik/core/sources/flow_manager.py:323
#, python-format #, python-format
msgid "Successfully authenticated with %(source)s!" msgid "Successfully authenticated with %(source)s!"
msgstr "Authentifié avec succès avec %(source)s!" msgstr "Authentifié avec succès avec %(source)s!"
#: authentik/core/sources/flow_manager.py:299 #: authentik/core/sources/flow_manager.py:295
#, python-format #, python-format
msgid "Successfully linked %(source)s!" msgid "Successfully linked %(source)s!"
msgstr "%(source)s lié avec succès!" msgstr "%(source)s lié avec succès!"
#: authentik/core/sources/flow_manager.py:318 #: authentik/core/sources/flow_manager.py:314
msgid "Source is not configured for enrollment." msgid "Source is not configured for enrollment."
msgstr "La source n'est pas configurée pour l'inscription." msgstr "La source n'est pas configurée pour l'inscription."
@ -334,12 +334,12 @@ msgstr ""
msgid "Go home" msgid "Go home"
msgstr "Retourner à l'accueil" msgstr "Retourner à l'accueil"
#: authentik/core/templates/login/base_full.html:90 #: authentik/core/templates/login/base_full.html:89
msgid "Powered by authentik" msgid "Powered by authentik"
msgstr "Propulsé par authentik" msgstr "Propulsé par authentik"
#: authentik/core/views/apps.py:53 #: authentik/core/views/apps.py:53
#: authentik/providers/oauth2/views/authorize.py:391 #: authentik/providers/oauth2/views/authorize.py:393
#: authentik/providers/oauth2/views/device_init.py:70 #: authentik/providers/oauth2/views/device_init.py:70
#: authentik/providers/saml/views/sso.py:70 #: authentik/providers/saml/views/sso.py:70
#, python-format #, python-format
@ -370,6 +370,14 @@ msgstr "Paire de clé/certificat"
msgid "Certificate-Key Pairs" msgid "Certificate-Key Pairs"
msgstr "Paires de clé/certificat" msgstr "Paires de clé/certificat"
#: authentik/enterprise/models.py:193
msgid "License Usage"
msgstr "Utilisation de la licence"
#: authentik/enterprise/models.py:194
msgid "License Usage Records"
msgstr "Registre d'utilisation de la licence"
#: authentik/events/models.py:290 #: authentik/events/models.py:290
msgid "Event" msgid "Event"
msgstr "Évènement" msgstr "Évènement"
@ -479,7 +487,7 @@ msgstr "Mappage de Webhook"
msgid "Webhook Mappings" msgid "Webhook Mappings"
msgstr "Mappages de Webhook" msgstr "Mappages de Webhook"
#: authentik/events/monitored_tasks.py:198 #: authentik/events/monitored_tasks.py:205
msgid "Task has not been run yet." msgid "Task has not been run yet."
msgstr "Tâche pas encore exécutée." msgstr "Tâche pas encore exécutée."
@ -655,7 +663,7 @@ msgstr ""
msgid "Invalid kubeconfig" msgid "Invalid kubeconfig"
msgstr "kubeconfig invalide" msgstr "kubeconfig invalide"
#: authentik/outposts/models.py:121 #: authentik/outposts/models.py:122
msgid "" msgid ""
"If enabled, use the local connection. Required Docker socket/Kubernetes " "If enabled, use the local connection. Required Docker socket/Kubernetes "
"Integration" "Integration"
@ -663,15 +671,15 @@ msgstr ""
"Si activé, utilise la connexion locale. L'intégration Docker " "Si activé, utilise la connexion locale. L'intégration Docker "
"socket/Kubernetes est requise" "socket/Kubernetes est requise"
#: authentik/outposts/models.py:151 #: authentik/outposts/models.py:152
msgid "Outpost Service-Connection" msgid "Outpost Service-Connection"
msgstr "Connexion de service de l'avant-poste" msgstr "Connexion de service de l'avant-poste"
#: authentik/outposts/models.py:152 #: authentik/outposts/models.py:153
msgid "Outpost Service-Connections" msgid "Outpost Service-Connections"
msgstr "Connexions de service de l'avant-poste" msgstr "Connexions de service de l'avant-poste"
#: authentik/outposts/models.py:160 #: authentik/outposts/models.py:161
msgid "" msgid ""
"Can be in the format of 'unix://<path>' when connecting to a local docker " "Can be in the format of 'unix://<path>' when connecting to a local docker "
"daemon, or 'https://<hostname>:2376' when connecting to a remote system." "daemon, or 'https://<hostname>:2376' when connecting to a remote system."
@ -680,7 +688,7 @@ msgstr ""
" local, ou \"https://<hostname>:2376\" pour une connexion à un système " " local, ou \"https://<hostname>:2376\" pour une connexion à un système "
"distant." "distant."
#: authentik/outposts/models.py:172 #: authentik/outposts/models.py:173
msgid "" msgid ""
"CA which the endpoint's Certificate is verified against. Can be left empty " "CA which the endpoint's Certificate is verified against. Can be left empty "
"for no validation." "for no validation."
@ -688,7 +696,7 @@ msgstr ""
"AC auprès de laquelle le certificat du terminal est vérifié. Peut être " "AC auprès de laquelle le certificat du terminal est vérifié. Peut être "
"laissé vide en l'absence de validation." "laissé vide en l'absence de validation."
#: authentik/outposts/models.py:184 #: authentik/outposts/models.py:185
msgid "" msgid ""
"Certificate/Key used for authentication. Can be left empty for no " "Certificate/Key used for authentication. Can be left empty for no "
"authentication." "authentication."
@ -696,15 +704,15 @@ msgstr ""
"Certificat et clé utilisés pour l'authentification. Peut être laissé vide si" "Certificat et clé utilisés pour l'authentification. Peut être laissé vide si"
" pas d'authentification." " pas d'authentification."
#: authentik/outposts/models.py:202 #: authentik/outposts/models.py:203
msgid "Docker Service-Connection" msgid "Docker Service-Connection"
msgstr "Connexion de service Docker" msgstr "Connexion de service Docker"
#: authentik/outposts/models.py:203 #: authentik/outposts/models.py:204
msgid "Docker Service-Connections" msgid "Docker Service-Connections"
msgstr "Connexions de service Docker" msgstr "Connexions de service Docker"
#: authentik/outposts/models.py:211 #: authentik/outposts/models.py:212
msgid "" msgid ""
"Paste your kubeconfig here. authentik will automatically use the currently " "Paste your kubeconfig here. authentik will automatically use the currently "
"selected context." "selected context."
@ -712,19 +720,19 @@ msgstr ""
"Coller votre kubeconfig ici. authentik va automatiquement utiliseur le " "Coller votre kubeconfig ici. authentik va automatiquement utiliseur le "
"contexte actuellement sélectionné." "contexte actuellement sélectionné."
#: authentik/outposts/models.py:217 #: authentik/outposts/models.py:218
msgid "Verify SSL Certificates of the Kubernetes API endpoint" msgid "Verify SSL Certificates of the Kubernetes API endpoint"
msgstr "Vérifier les certificats SSL de l'API Kubernetes" msgstr "Vérifier les certificats SSL de l'API Kubernetes"
#: authentik/outposts/models.py:234 #: authentik/outposts/models.py:235
msgid "Kubernetes Service-Connection" msgid "Kubernetes Service-Connection"
msgstr "Connexion de service Kubernetes" msgstr "Connexion de service Kubernetes"
#: authentik/outposts/models.py:235 #: authentik/outposts/models.py:236
msgid "Kubernetes Service-Connections" msgid "Kubernetes Service-Connections"
msgstr "Connexions de service Kubernetes" msgstr "Connexions de service Kubernetes"
#: authentik/outposts/models.py:251 #: authentik/outposts/models.py:252
msgid "" msgid ""
"Select Service-Connection authentik should use to manage this outpost. Leave" "Select Service-Connection authentik should use to manage this outpost. Leave"
" empty if authentik should not handle the deployment." " empty if authentik should not handle the deployment."
@ -842,15 +850,19 @@ msgstr "Inverse la sortie de la politique. Les messages ne sont pas affectés."
msgid "Timeout after which Policy execution is terminated." msgid "Timeout after which Policy execution is terminated."
msgstr "Expiration après que l'exécution de la politique soit terminée." msgstr "Expiration après que l'exécution de la politique soit terminée."
#: authentik/policies/models.py:142 #: authentik/policies/models.py:92
msgid "Result if the Policy execution fails."
msgstr "Résultat si l'éxecution de la Politique échoue."
#: authentik/policies/models.py:145
msgid "Policy Binding" msgid "Policy Binding"
msgstr "Liaison de politique" msgstr "Liaison de politique"
#: authentik/policies/models.py:143 #: authentik/policies/models.py:146
msgid "Policy Bindings" msgid "Policy Bindings"
msgstr "Liaisons des politiques" msgstr "Liaisons des politiques"
#: authentik/policies/models.py:164 #: authentik/policies/models.py:167
msgid "" msgid ""
"When this option is enabled, all executions of this policy will be logged. " "When this option is enabled, all executions of this policy will be logged. "
"By default, only execution errors are logged." "By default, only execution errors are logged."
@ -858,11 +870,11 @@ msgstr ""
"Si activée, toutes les exécutions de cette politique seront enregistrées. " "Si activée, toutes les exécutions de cette politique seront enregistrées. "
"Par défaut, seules les erreurs d'exécution sont consignées." "Par défaut, seules les erreurs d'exécution sont consignées."
#: authentik/policies/models.py:186 #: authentik/policies/models.py:189
msgid "Policy" msgid "Policy"
msgstr "Politique" msgstr "Politique"
#: authentik/policies/models.py:187 #: authentik/policies/models.py:190
msgid "Policies" msgid "Policies"
msgstr "Politiques" msgstr "Politiques"
@ -906,14 +918,26 @@ msgstr "Politique de Mots de Passe"
msgid "Password Policies" msgid "Password Policies"
msgstr "Politiques de Mot de Passe" msgstr "Politiques de Mot de Passe"
#: authentik/policies/reputation/models.py:58 #: authentik/policies/reputation/api.py:18
msgid "Either IP or Username must be checked"
msgstr "L'IP ou le nom d'utilisateur doit être vérifé"
#: authentik/policies/reputation/models.py:67
msgid "Reputation Policy" msgid "Reputation Policy"
msgstr "Politique de Réputation" msgstr "Politique de Réputation"
#: authentik/policies/reputation/models.py:59 #: authentik/policies/reputation/models.py:68
msgid "Reputation Policies" msgid "Reputation Policies"
msgstr "Politiques de Réputation" msgstr "Politiques de Réputation"
#: authentik/policies/reputation/models.py:95
msgid "Reputation Score"
msgstr "Score de Réputation"
#: authentik/policies/reputation/models.py:96
msgid "Reputation Scores"
msgstr "Scores de Réputation"
#: authentik/policies/templates/policies/denied.html:7 #: authentik/policies/templates/policies/denied.html:7
#: authentik/policies/templates/policies/denied.html:11 #: authentik/policies/templates/policies/denied.html:11
msgid "Permission denied" msgid "Permission denied"
@ -1043,65 +1067,65 @@ msgstr ""
"attribut \"upn\" renseigné. Utiliser cette méthode seulement si les domaines" "attribut \"upn\" renseigné. Utiliser cette méthode seulement si les domaines"
" UPN et courriel sont différents." " UPN et courriel sont différents."
#: authentik/providers/oauth2/models.py:42 #: authentik/providers/oauth2/models.py:43
msgid "Confidential" msgid "Confidential"
msgstr "Confidentiel" msgstr "Confidentiel"
#: authentik/providers/oauth2/models.py:43 #: authentik/providers/oauth2/models.py:44
msgid "Public" msgid "Public"
msgstr "Public" msgstr "Public"
#: authentik/providers/oauth2/models.py:65 #: authentik/providers/oauth2/models.py:66
msgid "Same identifier is used for all providers" msgid "Same identifier is used for all providers"
msgstr "Le même identifiant est utilisé pour tous les fournisseurs" msgstr "Le même identifiant est utilisé pour tous les fournisseurs"
#: authentik/providers/oauth2/models.py:67 #: authentik/providers/oauth2/models.py:68
msgid "Each provider has a different issuer, based on the application slug." msgid "Each provider has a different issuer, based on the application slug."
msgstr "" msgstr ""
"Chaque fournisseur a un émetteur différent, basé sur le slug de " "Chaque fournisseur a un émetteur différent, basé sur le slug de "
"l'application." "l'application."
#: authentik/providers/oauth2/models.py:74 #: authentik/providers/oauth2/models.py:75
msgid "code (Authorization Code Flow)" msgid "code (Authorization Code Flow)"
msgstr "code (Authorization Code Flow)" msgstr "code (Authorization Code Flow)"
#: authentik/providers/oauth2/models.py:75 #: authentik/providers/oauth2/models.py:76
msgid "id_token (Implicit Flow)" msgid "id_token (Implicit Flow)"
msgstr "id_token (Implicit Flow)" msgstr "id_token (Implicit Flow)"
#: authentik/providers/oauth2/models.py:76 #: authentik/providers/oauth2/models.py:77
msgid "id_token token (Implicit Flow)" msgid "id_token token (Implicit Flow)"
msgstr "id_token token (Implicit Flow)" msgstr "id_token token (Implicit Flow)"
#: authentik/providers/oauth2/models.py:77 #: authentik/providers/oauth2/models.py:78
msgid "code token (Hybrid Flow)" msgid "code token (Hybrid Flow)"
msgstr "code token (Hybrid Flow)" msgstr "code token (Hybrid Flow)"
#: authentik/providers/oauth2/models.py:78 #: authentik/providers/oauth2/models.py:79
msgid "code id_token (Hybrid Flow)" msgid "code id_token (Hybrid Flow)"
msgstr "code id_token (Hybrid Flow)" msgstr "code id_token (Hybrid Flow)"
#: authentik/providers/oauth2/models.py:79 #: authentik/providers/oauth2/models.py:80
msgid "code id_token token (Hybrid Flow)" msgid "code id_token token (Hybrid Flow)"
msgstr "code id_token token (Hybrid Flow)" msgstr "code id_token token (Hybrid Flow)"
#: authentik/providers/oauth2/models.py:85 #: authentik/providers/oauth2/models.py:86
msgid "HS256 (Symmetric Encryption)" msgid "HS256 (Symmetric Encryption)"
msgstr "HS256 (chiffrement symétrique)" msgstr "HS256 (chiffrement symétrique)"
#: authentik/providers/oauth2/models.py:86 #: authentik/providers/oauth2/models.py:87
msgid "RS256 (Asymmetric Encryption)" msgid "RS256 (Asymmetric Encryption)"
msgstr "RS256 (chiffrement asymétrique)" msgstr "RS256 (chiffrement asymétrique)"
#: authentik/providers/oauth2/models.py:87 #: authentik/providers/oauth2/models.py:88
msgid "ES256 (Asymmetric Encryption)" msgid "ES256 (Asymmetric Encryption)"
msgstr "ES256 (Chiffrement Asymétrique)" msgstr "ES256 (Chiffrement Asymétrique)"
#: authentik/providers/oauth2/models.py:93 #: authentik/providers/oauth2/models.py:94
msgid "Scope used by the client" msgid "Scope used by the client"
msgstr "Portées utilisées par le client" msgstr "Portées utilisées par le client"
#: authentik/providers/oauth2/models.py:97 #: authentik/providers/oauth2/models.py:98
msgid "" msgid ""
"Description shown to the user when consenting. If left empty, the user won't" "Description shown to the user when consenting. If left empty, the user won't"
" be informed." " be informed."
@ -1109,19 +1133,19 @@ msgstr ""
"Description montrée à l'utilisateur lors de l'approbation. Aucune " "Description montrée à l'utilisateur lors de l'approbation. Aucune "
"information présentée à l'utilisateur si laissé vide." "information présentée à l'utilisateur si laissé vide."
#: authentik/providers/oauth2/models.py:116 #: authentik/providers/oauth2/models.py:117
msgid "Scope Mapping" msgid "Scope Mapping"
msgstr "Mappage de Portée" msgstr "Mappage de Portée"
#: authentik/providers/oauth2/models.py:117 #: authentik/providers/oauth2/models.py:118
msgid "Scope Mappings" msgid "Scope Mappings"
msgstr "Mappage de Portée" msgstr "Mappage de Portée"
#: authentik/providers/oauth2/models.py:127 #: authentik/providers/oauth2/models.py:128
msgid "Client Type" msgid "Client Type"
msgstr "Type de Client" msgstr "Type de Client"
#: authentik/providers/oauth2/models.py:129 #: authentik/providers/oauth2/models.py:130
msgid "" msgid ""
"Confidential clients are capable of maintaining the confidentiality of their" "Confidential clients are capable of maintaining the confidentiality of their"
" credentials. Public clients are incapable" " credentials. Public clients are incapable"
@ -1129,27 +1153,27 @@ msgstr ""
"Les clients confidentiels sont capable de maintenir la confidentialité de " "Les clients confidentiels sont capable de maintenir la confidentialité de "
"leurs identifiants. Les clients publics n'en sont pas capables." "leurs identifiants. Les clients publics n'en sont pas capables."
#: authentik/providers/oauth2/models.py:136 #: authentik/providers/oauth2/models.py:137
msgid "Client ID" msgid "Client ID"
msgstr "ID client" msgstr "ID client"
#: authentik/providers/oauth2/models.py:142 #: authentik/providers/oauth2/models.py:143
msgid "Client Secret" msgid "Client Secret"
msgstr "Secret du client" msgstr "Secret du client"
#: authentik/providers/oauth2/models.py:148 #: authentik/providers/oauth2/models.py:149
msgid "Redirect URIs" msgid "Redirect URIs"
msgstr "URIs de redirection" msgstr "URIs de redirection"
#: authentik/providers/oauth2/models.py:149 #: authentik/providers/oauth2/models.py:150
msgid "Enter each URI on a new line." msgid "Enter each URI on a new line."
msgstr "Entrez chaque URI sur une nouvelle ligne." msgstr "Entrez chaque URI sur une nouvelle ligne."
#: authentik/providers/oauth2/models.py:154 #: authentik/providers/oauth2/models.py:155
msgid "Include claims in id_token" msgid "Include claims in id_token"
msgstr "Include les demandes utilisateurs dans id_token" msgstr "Include les demandes utilisateurs dans id_token"
#: authentik/providers/oauth2/models.py:156 #: authentik/providers/oauth2/models.py:157
msgid "" msgid ""
"Include User claims from scopes in the id_token, for applications that don't" "Include User claims from scopes in the id_token, for applications that don't"
" access the userinfo endpoint." " access the userinfo endpoint."
@ -1157,7 +1181,7 @@ msgstr ""
"Inclure depuis la portée les demandes utilisateurs dans id_token, pour les " "Inclure depuis la portée les demandes utilisateurs dans id_token, pour les "
"applications qui n'accèdent pas au point de terminaison userinfo." "applications qui n'accèdent pas au point de terminaison userinfo."
#: authentik/providers/oauth2/models.py:165 #: authentik/providers/oauth2/models.py:166
msgid "" msgid ""
"Access codes not valid on or after current time + this value (Format: " "Access codes not valid on or after current time + this value (Format: "
"hours=1;minutes=2;seconds=3)." "hours=1;minutes=2;seconds=3)."
@ -1165,8 +1189,8 @@ msgstr ""
"Les codes d'accès ne seront plus valide à partir de l'heure actuelle + cette" "Les codes d'accès ne seront plus valide à partir de l'heure actuelle + cette"
" valeur (Format : hours=1;minutes=2;seconds=3)." " valeur (Format : hours=1;minutes=2;seconds=3)."
#: authentik/providers/oauth2/models.py:173 #: authentik/providers/oauth2/models.py:174
#: authentik/providers/oauth2/models.py:181 #: authentik/providers/oauth2/models.py:182
msgid "" msgid ""
"Tokens not valid on or after current time + this value (Format: " "Tokens not valid on or after current time + this value (Format: "
"hours=1;minutes=2;seconds=3)." "hours=1;minutes=2;seconds=3)."
@ -1174,7 +1198,7 @@ msgstr ""
"Les jetons ne seront plus valides à partir de l'heure actuelle + cette " "Les jetons ne seront plus valides à partir de l'heure actuelle + cette "
"valeur (Format: hours=1;minutes=2;seconds=3)." "valeur (Format: hours=1;minutes=2;seconds=3)."
#: authentik/providers/oauth2/models.py:190 #: authentik/providers/oauth2/models.py:191
msgid "" msgid ""
"Configure what data should be used as unique User Identifier. For most " "Configure what data should be used as unique User Identifier. For most "
"cases, the default should be fine." "cases, the default should be fine."
@ -1182,15 +1206,15 @@ msgstr ""
"Configure quelle donnée utiliser pour l'identifiant unique utilisateur. La " "Configure quelle donnée utiliser pour l'identifiant unique utilisateur. La "
"valeur par défaut devrait être correcte dans la plupart des cas." "valeur par défaut devrait être correcte dans la plupart des cas."
#: authentik/providers/oauth2/models.py:197 #: authentik/providers/oauth2/models.py:198
msgid "Configure how the issuer field of the ID Token should be filled." msgid "Configure how the issuer field of the ID Token should be filled."
msgstr "Configure comment le champ émetteur du jeton ID sera rempli." msgstr "Configure comment le champ émetteur du jeton ID sera rempli."
#: authentik/providers/oauth2/models.py:202 #: authentik/providers/oauth2/models.py:203
msgid "Signing Key" msgid "Signing Key"
msgstr "Clé de signature" msgstr "Clé de signature"
#: authentik/providers/oauth2/models.py:206 #: authentik/providers/oauth2/models.py:207
msgid "" msgid ""
"Key used to sign the tokens. Only required when JWT Algorithm is set to " "Key used to sign the tokens. Only required when JWT Algorithm is set to "
"RS256." "RS256."
@ -1198,7 +1222,7 @@ msgstr ""
"Clé utilisée pour signer les jetons. Nécessaire uniquement lorsque " "Clé utilisée pour signer les jetons. Nécessaire uniquement lorsque "
"l'algorithme JWT est réglé sur RS256." "l'algorithme JWT est réglé sur RS256."
#: authentik/providers/oauth2/models.py:213 #: authentik/providers/oauth2/models.py:214
msgid "" msgid ""
"Any JWT signed by the JWK of the selected source can be used to " "Any JWT signed by the JWK of the selected source can be used to "
"authenticate." "authenticate."
@ -1206,72 +1230,72 @@ msgstr ""
"Tout JWT signé par le JWK de la source sélectionnée peut être utilisé pour " "Tout JWT signé par le JWK de la source sélectionnée peut être utilisé pour "
"s'authentifier." "s'authentifier."
#: authentik/providers/oauth2/models.py:286 #: authentik/providers/oauth2/models.py:287
msgid "OAuth2/OpenID Provider" msgid "OAuth2/OpenID Provider"
msgstr "Fournisseur OAuth2/OpenID" msgstr "Fournisseur OAuth2/OpenID"
#: authentik/providers/oauth2/models.py:287 #: authentik/providers/oauth2/models.py:288
msgid "OAuth2/OpenID Providers" msgid "OAuth2/OpenID Providers"
msgstr "Fournisseurs OAuth2/OpenID" msgstr "Fournisseurs OAuth2/OpenID"
#: authentik/providers/oauth2/models.py:296 #: authentik/providers/oauth2/models.py:297
#: authentik/providers/oauth2/models.py:428 #: authentik/providers/oauth2/models.py:429
msgid "Scopes" msgid "Scopes"
msgstr "Portées" msgstr "Portées"
#: authentik/providers/oauth2/models.py:315 #: authentik/providers/oauth2/models.py:316
msgid "Code" msgid "Code"
msgstr "Code" msgstr "Code"
#: authentik/providers/oauth2/models.py:316 #: authentik/providers/oauth2/models.py:317
msgid "Nonce" msgid "Nonce"
msgstr "Nonce" msgstr "Nonce"
#: authentik/providers/oauth2/models.py:317 #: authentik/providers/oauth2/models.py:318
msgid "Code Challenge" msgid "Code Challenge"
msgstr "Challenge à code" msgstr "Challenge à code"
#: authentik/providers/oauth2/models.py:319 #: authentik/providers/oauth2/models.py:320
msgid "Code Challenge Method" msgid "Code Challenge Method"
msgstr "Méthode de challenge à code" msgstr "Méthode de challenge à code"
#: authentik/providers/oauth2/models.py:339 #: authentik/providers/oauth2/models.py:340
msgid "Authorization Code" msgid "Authorization Code"
msgstr "Code d'autorisation" msgstr "Code d'autorisation"
#: authentik/providers/oauth2/models.py:340 #: authentik/providers/oauth2/models.py:341
msgid "Authorization Codes" msgid "Authorization Codes"
msgstr "Codes d'autorisation" msgstr "Codes d'autorisation"
#: authentik/providers/oauth2/models.py:382 #: authentik/providers/oauth2/models.py:383
msgid "OAuth2 Access Token" msgid "OAuth2 Access Token"
msgstr "Jeton d'accès OAuth2" msgstr "Jeton d'accès OAuth2"
#: authentik/providers/oauth2/models.py:383 #: authentik/providers/oauth2/models.py:384
msgid "OAuth2 Access Tokens" msgid "OAuth2 Access Tokens"
msgstr "Jetons d'accès OAuth2" msgstr "Jetons d'accès OAuth2"
#: authentik/providers/oauth2/models.py:393 #: authentik/providers/oauth2/models.py:394
msgid "ID Token" msgid "ID Token"
msgstr "ID du jeton" msgstr "ID du jeton"
#: authentik/providers/oauth2/models.py:412 #: authentik/providers/oauth2/models.py:413
msgid "OAuth2 Refresh Token" msgid "OAuth2 Refresh Token"
msgstr "Jeton de rafraîchissement OAuth2" msgstr "Jeton de rafraîchissement OAuth2"
#: authentik/providers/oauth2/models.py:413 #: authentik/providers/oauth2/models.py:414
msgid "OAuth2 Refresh Tokens" msgid "OAuth2 Refresh Tokens"
msgstr "Jetons de rafraîchissement OAuth2" msgstr "Jetons de rafraîchissement OAuth2"
#: authentik/providers/oauth2/models.py:440 #: authentik/providers/oauth2/models.py:441
msgid "Device Token" msgid "Device Token"
msgstr "Jeton d'équipement" msgstr "Jeton d'équipement"
#: authentik/providers/oauth2/models.py:441 #: authentik/providers/oauth2/models.py:442
msgid "Device Tokens" msgid "Device Tokens"
msgstr "Jetons d'équipement" msgstr "Jetons d'équipement"
#: authentik/providers/oauth2/views/authorize.py:446 #: authentik/providers/oauth2/views/authorize.py:448
#: authentik/providers/saml/views/flows.py:87 #: authentik/providers/saml/views/flows.py:87
#, python-format #, python-format
msgid "Redirecting to %(app)s..." msgid "Redirecting to %(app)s..."
@ -1281,20 +1305,20 @@ msgstr "Redirection vers %(app)s..."
msgid "Invalid code" msgid "Invalid code"
msgstr "Code invalide" msgstr "Code invalide"
#: authentik/providers/oauth2/views/userinfo.py:51 #: authentik/providers/oauth2/views/userinfo.py:55
#: authentik/providers/oauth2/views/userinfo.py:52 #: authentik/providers/oauth2/views/userinfo.py:56
msgid "GitHub Compatibility: Access your User Information" msgid "GitHub Compatibility: Access your User Information"
msgstr "Compatibilité GitHub : accès aux informations utilisateur" msgstr "Compatibilité GitHub : accès aux informations utilisateur"
#: authentik/providers/oauth2/views/userinfo.py:53 #: authentik/providers/oauth2/views/userinfo.py:57
msgid "GitHub Compatibility: Access you Email addresses" msgid "GitHub Compatibility: Access you Email addresses"
msgstr "Compatibilité GitHub : accès aux adresses email" msgstr "Compatibilité GitHub : accès aux adresses email"
#: authentik/providers/oauth2/views/userinfo.py:54 #: authentik/providers/oauth2/views/userinfo.py:58
msgid "GitHub Compatibility: Access your Groups" msgid "GitHub Compatibility: Access your Groups"
msgstr "Compatibilité GitHub : accès aux groupes" msgstr "Compatibilité GitHub : accès aux groupes"
#: authentik/providers/oauth2/views/userinfo.py:55 #: authentik/providers/oauth2/views/userinfo.py:59
msgid "authentik API Access on behalf of your user" msgid "authentik API Access on behalf of your user"
msgstr "Accès à l'API authentik au nom des utilisateurs" msgstr "Accès à l'API authentik au nom des utilisateurs"
@ -1304,7 +1328,7 @@ msgstr ""
"Les attributs utilisateur et mot de passe doivent être définis lorsque " "Les attributs utilisateur et mot de passe doivent être définis lorsque "
"l'authentification basique est activée." "l'authentification basique est activée."
#: authentik/providers/proxy/api.py:62 #: authentik/providers/proxy/api.py:63
msgid "Internal host cannot be empty when forward auth is disabled." msgid "Internal host cannot be empty when forward auth is disabled."
msgstr "" msgstr ""
"L'hôte interne ne peut pas être vide lorsque le transfert d'authentification" "L'hôte interne ne peut pas être vide lorsque le transfert d'authentification"
@ -1411,11 +1435,11 @@ msgstr "Fournisseur Radius"
msgid "Radius Providers" msgid "Radius Providers"
msgstr "Fournisseurs Radius" msgstr "Fournisseurs Radius"
#: authentik/providers/saml/api/providers.py:260 #: authentik/providers/saml/api/providers.py:257
msgid "Invalid XML Syntax" msgid "Invalid XML Syntax"
msgstr "Syntaxe XML Invalide" msgstr "Syntaxe XML Invalide"
#: authentik/providers/saml/api/providers.py:270 #: authentik/providers/saml/api/providers.py:267
#, python-format #, python-format
msgid "Failed to import Metadata: %(message)s" msgid "Failed to import Metadata: %(message)s"
msgstr "Échec d'import des metadata : %(message)s" msgstr "Échec d'import des metadata : %(message)s"
@ -2084,8 +2108,8 @@ msgid "SMS Devices"
msgstr "Appareils SMS" msgstr "Appareils SMS"
#: authentik/stages/authenticator_sms/stage.py:55 #: authentik/stages/authenticator_sms/stage.py:55
#: authentik/stages/authenticator_totp/stage.py:42 #: authentik/stages/authenticator_totp/stage.py:41
#: authentik/stages/authenticator_totp/stage.py:45 #: authentik/stages/authenticator_totp/stage.py:44
msgid "Code does not match" msgid "Code does not match"
msgstr "Le Code ne correspond pas" msgstr "Le Code ne correspond pas"
@ -2093,33 +2117,49 @@ msgstr "Le Code ne correspond pas"
msgid "Invalid phone number" msgid "Invalid phone number"
msgstr "Numéro de téléphone invalide" msgstr "Numéro de téléphone invalide"
#: authentik/stages/authenticator_static/models.py:46 #: authentik/stages/authenticator_static/models.py:52
msgid "Static Authenticator Stage" msgid "Static Authenticator Stage"
msgstr "Étape de configuration de l'authentificateur statique" msgstr "Étape de configuration de l'authentificateur statique"
#: authentik/stages/authenticator_static/models.py:47 #: authentik/stages/authenticator_static/models.py:53
msgid "Static Authenticator Stages" msgid "Static Authenticator Stages"
msgstr "Étapes de configuration de l'authentificateur statique" msgstr "Étapes de configuration de l'authentificateur statique"
#: authentik/stages/authenticator_totp/models.py:16 #: authentik/stages/authenticator_static/models.py:98
msgid "Static device"
msgstr "Équipement statique"
#: authentik/stages/authenticator_static/models.py:99
msgid "Static devices"
msgstr "Équipements statiques"
#: authentik/stages/authenticator_totp/models.py:25
msgid "6 digits, widely compatible" msgid "6 digits, widely compatible"
msgstr "6 chiffres, compatibilité large" msgstr "6 chiffres, compatibilité large"
#: authentik/stages/authenticator_totp/models.py:17 #: authentik/stages/authenticator_totp/models.py:26
msgid "8 digits, not compatible with apps like Google Authenticator" msgid "8 digits, not compatible with apps like Google Authenticator"
msgstr "" msgstr ""
"8 chiffres, incompatible avec certaines applications telles que Google " "8 chiffres, incompatible avec certaines applications telles que Google "
"Authenticator" "Authenticator"
#: authentik/stages/authenticator_totp/models.py:53 #: authentik/stages/authenticator_totp/models.py:62
msgid "TOTP Authenticator Setup Stage" msgid "TOTP Authenticator Setup Stage"
msgstr "Étape de configuration de l'authentificateur TOTP" msgstr "Étape de configuration de l'authentificateur TOTP"
#: authentik/stages/authenticator_totp/models.py:54 #: authentik/stages/authenticator_totp/models.py:63
msgid "TOTP Authenticator Setup Stages" msgid "TOTP Authenticator Setup Stages"
msgstr "Étapes de configuration de l'authentificateur TOTP" msgstr "Étapes de configuration de l'authentificateur TOTP"
#: authentik/stages/authenticator_validate/challenge.py:123 #: authentik/stages/authenticator_totp/models.py:244
msgid "TOTP device"
msgstr "Équipement TOTP"
#: authentik/stages/authenticator_totp/models.py:245
msgid "TOTP devices"
msgstr "Équipements TOTP"
#: authentik/stages/authenticator_validate/challenge.py:131
msgid "Invalid Token" msgid "Invalid Token"
msgstr "Jeton Invalide" msgstr "Jeton Invalide"
@ -2279,15 +2319,15 @@ msgstr "Étape Email"
msgid "Email Stages" msgid "Email Stages"
msgstr "Étape Email" msgstr "Étape Email"
#: authentik/stages/email/stage.py:112 #: authentik/stages/email/stage.py:117
msgid "Successfully verified Email." msgid "Successfully verified Email."
msgstr "Email vérifié avec succès." msgstr "Email vérifié avec succès."
#: authentik/stages/email/stage.py:119 authentik/stages/email/stage.py:141 #: authentik/stages/email/stage.py:124 authentik/stages/email/stage.py:146
msgid "No pending user." msgid "No pending user."
msgstr "Pas d'utilisateurs en attente." msgstr "Pas d'utilisateurs en attente."
#: authentik/stages/email/stage.py:131 #: authentik/stages/email/stage.py:136
msgid "Email sent." msgid "Email sent."
msgstr "Email envoyé." msgstr "Email envoyé."
@ -2433,11 +2473,11 @@ msgstr "Étape d'identification"
msgid "Identification Stages" msgid "Identification Stages"
msgstr "Étapes d'identification" msgstr "Étapes d'identification"
#: authentik/stages/identification/stage.py:184 #: authentik/stages/identification/stage.py:188
msgid "Log in" msgid "Log in"
msgstr "Se connecter" msgstr "Se connecter"
#: authentik/stages/identification/stage.py:185 #: authentik/stages/identification/stage.py:189
msgid "Continue" msgid "Continue"
msgstr "Continuer" msgstr "Continuer"
@ -2480,7 +2520,7 @@ msgstr "Invitation"
msgid "Invitations" msgid "Invitations"
msgstr "Invitations" msgstr "Invitations"
#: authentik/stages/invitation/stage.py:66 #: authentik/stages/invitation/stage.py:62
msgid "Invalid invite/invite not found" msgid "Invalid invite/invite not found"
msgstr "Invitation invalide/invitation introuvable" msgstr "Invitation invalide/invitation introuvable"
@ -2517,7 +2557,7 @@ msgstr "Étape de mot de passe"
msgid "Password Stages" msgid "Password Stages"
msgstr "Étapes de mot de passe" msgstr "Étapes de mot de passe"
#: authentik/stages/password/stage.py:159 #: authentik/stages/password/stage.py:124
msgid "Invalid password" msgid "Invalid password"
msgstr "Mot de passe invalide" msgstr "Mot de passe invalide"
@ -2645,7 +2685,7 @@ msgstr "Étape de suppression utilisateur"
msgid "User Delete Stages" msgid "User Delete Stages"
msgstr "Étapes de suppression utilisateur" msgstr "Étapes de suppression utilisateur"
#: authentik/stages/user_delete/stage.py:22 #: authentik/stages/user_delete/stage.py:18
msgid "No Pending User." msgid "No Pending User."
msgstr "Aucun utilisateur en attente." msgstr "Aucun utilisateur en attente."
@ -2682,11 +2722,11 @@ msgstr "Étape de connexion utlisateur"
msgid "User Login Stages" msgid "User Login Stages"
msgstr "Étapes de connexion utilisateur" msgstr "Étapes de connexion utilisateur"
#: authentik/stages/user_login/stage.py:63 #: authentik/stages/user_login/stage.py:57
msgid "No Pending user to login." msgid "No Pending user to login."
msgstr "Pas d'utilisateurs en attente à connecter." msgstr "Pas d'utilisateurs en attente à connecter."
#: authentik/stages/user_login/stage.py:96 #: authentik/stages/user_login/stage.py:90
msgid "Successfully logged in!" msgid "Successfully logged in!"
msgstr "Connexion réussie !" msgstr "Connexion réussie !"
@ -2716,16 +2756,16 @@ msgstr "Étapes d'écriture utilisateur"
msgid "User Write Stages" msgid "User Write Stages"
msgstr "Étapes d'écriture utilisateur" msgstr "Étapes d'écriture utilisateur"
#: authentik/stages/user_write/stage.py:134 #: authentik/stages/user_write/stage.py:130
msgid "No Pending data." msgid "No Pending data."
msgstr "Aucune donnée en attente." msgstr "Aucune donnée en attente."
#: authentik/stages/user_write/stage.py:140 #: authentik/stages/user_write/stage.py:136
msgid "No user found and can't create new user." msgid "No user found and can't create new user."
msgstr "Utilisateur introuvable et impossible de créer un nouvel utilisateur." msgstr "Utilisateur introuvable et impossible de créer un nouvel utilisateur."
#: authentik/stages/user_write/stage.py:157 #: authentik/stages/user_write/stage.py:153
#: authentik/stages/user_write/stage.py:171 #: authentik/stages/user_write/stage.py:167
msgid "Failed to update user. Please try again later." msgid "Failed to update user. Please try again later."
msgstr "" msgstr ""
"Échec de mise à jour de l'utilisateur. Merci de réessayer ultérieurement," "Échec de mise à jour de l'utilisateur. Merci de réessayer ultérieurement,"

310
poetry.lock generated
View File

@ -265,13 +265,13 @@ files = [
[[package]] [[package]]
name = "astroid" name = "astroid"
version = "2.15.6" version = "2.15.7"
description = "An abstract syntax tree for Python with inference support." description = "An abstract syntax tree for Python with inference support."
optional = false optional = false
python-versions = ">=3.7.2" python-versions = ">=3.7.2"
files = [ files = [
{file = "astroid-2.15.6-py3-none-any.whl", hash = "sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c"}, {file = "astroid-2.15.7-py3-none-any.whl", hash = "sha256:958f280532e36ca84a13023f15cb1556fb6792d193acb87e1f3ca536b6fa6bd2"},
{file = "astroid-2.15.6.tar.gz", hash = "sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd"}, {file = "astroid-2.15.7.tar.gz", hash = "sha256:c522f2832a900e27a7d284b9b6ef670d2495f760ede3c8c0b004a5641d3c5987"},
] ]
[package.dependencies] [package.dependencies]
@ -1219,13 +1219,13 @@ hiredis = ["redis[hiredis] (>=3,!=4.0.0,!=4.0.1)"]
[[package]] [[package]]
name = "django-silk" name = "django-silk"
version = "5.0.3" version = "5.0.4"
description = "Silky smooth profiling for the Django Framework" description = "Silky smooth profiling for the Django Framework"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.8"
files = [ files = [
{file = "django-silk-5.0.3.tar.gz", hash = "sha256:2f1fcaaf21192011147537fe1ca72dc9f552f32d7043ebd82aeeda370f194469"}, {file = "django-silk-5.0.4.tar.gz", hash = "sha256:8cbfbc647d182527726d8d52d3fcfa193f4d250f21406c3fb1062efa6fb95c63"},
{file = "django_silk-5.0.3-py3-none-any.whl", hash = "sha256:50552f06d9306d06517fbeab9a2c74856355e06304f03ed16b6dd353f7c77e7a"}, {file = "django_silk-5.0.4-py3-none-any.whl", hash = "sha256:b345d3973d1d382e09735eb525eaf3eebd3edee9a69d1003eb9b01badb2438db"},
] ]
[package.dependencies] [package.dependencies]
@ -1330,13 +1330,13 @@ tests = ["black", "django-stubs[compatible-mypy]", "djangorestframework-stubs[co
[[package]] [[package]]
name = "drf-spectacular" name = "drf-spectacular"
version = "0.26.4" version = "0.26.5"
description = "Sane and flexible OpenAPI 3 schema generation for Django REST framework" description = "Sane and flexible OpenAPI 3 schema generation for Django REST framework"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
{file = "drf-spectacular-0.26.4.tar.gz", hash = "sha256:8f5a8f87353d1bb8dcb3f3909b7109b2dcbe1d91f3e069409cf322963e140bd6"}, {file = "drf-spectacular-0.26.5.tar.gz", hash = "sha256:aee55330a774ba8a9cbdb125714d1c9ee05a8aafd3ce3be8bfd26527649aeb44"},
{file = "drf_spectacular-0.26.4-py3-none-any.whl", hash = "sha256:afeccc6533dcdb4e78afbfcc49f3c5e9c369aeb62f965e4d1a43b165449c147a"}, {file = "drf_spectacular-0.26.5-py3-none-any.whl", hash = "sha256:c0002a820b11771fdbf37853deb371947caf0159d1afeeffe7598e964bc1db94"},
] ]
[package.dependencies] [package.dependencies]
@ -2570,36 +2570,36 @@ wcwidth = "*"
[[package]] [[package]]
name = "psycopg" name = "psycopg"
version = "3.1.10" version = "3.1.12"
description = "PostgreSQL database adapter for Python" description = "PostgreSQL database adapter for Python"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "psycopg-3.1.10-py3-none-any.whl", hash = "sha256:8bbeddae5075c7890b2fa3e3553440376d3c5e28418335dee3c3656b06fa2b52"}, {file = "psycopg-3.1.12-py3-none-any.whl", hash = "sha256:8ec5230d6a7eb654b4fb3cf2d3eda8871d68f24807b934790504467f1deee9f8"},
{file = "psycopg-3.1.10.tar.gz", hash = "sha256:15b25741494344c24066dc2479b0f383dd1b82fa5e75612fa4fa5bb30726e9b6"}, {file = "psycopg-3.1.12.tar.gz", hash = "sha256:cec7ad2bc6a8510e56c45746c631cf9394148bdc8a9a11fd8cf8554ce129ae78"},
] ]
[package.dependencies] [package.dependencies]
psycopg-c = {version = "3.1.10", optional = true, markers = "extra == \"c\""} psycopg-c = {version = "3.1.12", optional = true, markers = "extra == \"c\""}
typing-extensions = ">=4.1" typing-extensions = ">=4.1"
tzdata = {version = "*", markers = "sys_platform == \"win32\""} tzdata = {version = "*", markers = "sys_platform == \"win32\""}
[package.extras] [package.extras]
binary = ["psycopg-binary (==3.1.10)"] binary = ["psycopg-binary (==3.1.12)"]
c = ["psycopg-c (==3.1.10)"] c = ["psycopg-c (==3.1.12)"]
dev = ["black (>=23.1.0)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.4.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"] dev = ["black (>=23.1.0)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.4.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"]
docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"]
pool = ["psycopg-pool"] pool = ["psycopg-pool"]
test = ["anyio (>=3.6.2)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] test = ["anyio (>=3.6.2,<4.0)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"]
[[package]] [[package]]
name = "psycopg-c" name = "psycopg-c"
version = "3.1.10" version = "3.1.12"
description = "PostgreSQL database adapter for Python -- C optimisation distribution" description = "PostgreSQL database adapter for Python -- C optimisation distribution"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "psycopg-c-3.1.10.tar.gz", hash = "sha256:0c923cfac7a8a3796c915c253f51b7c667358492924cbccecc18df9e79b103e9"}, {file = "psycopg-c-3.1.12.tar.gz", hash = "sha256:81db07874c7c530482d07155d144b287b47260dd1782a0d2d3ca7ae2d4641686"},
] ]
[[package]] [[package]]
@ -2692,19 +2692,19 @@ files = [
[[package]] [[package]]
name = "pydantic" name = "pydantic"
version = "2.3.0" version = "2.4.2"
description = "Data validation using Python type hints" description = "Data validation using Python type hints"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "pydantic-2.3.0-py3-none-any.whl", hash = "sha256:45b5e446c6dfaad9444819a293b921a40e1db1aa61ea08aede0522529ce90e81"}, {file = "pydantic-2.4.2-py3-none-any.whl", hash = "sha256:bc3ddf669d234f4220e6e1c4d96b061abe0998185a8d7855c0126782b7abc8c1"},
{file = "pydantic-2.3.0.tar.gz", hash = "sha256:1607cc106602284cd4a00882986570472f193fde9cb1259bceeaedb26aa79a6d"}, {file = "pydantic-2.4.2.tar.gz", hash = "sha256:94f336138093a5d7f426aac732dcfe7ab4eb4da243c88f891d65deb4a2556ee7"},
] ]
[package.dependencies] [package.dependencies]
annotated-types = ">=0.4.0" annotated-types = ">=0.4.0"
email-validator = {version = ">=2.0.0", optional = true, markers = "extra == \"email\""} email-validator = {version = ">=2.0.0", optional = true, markers = "extra == \"email\""}
pydantic-core = "2.6.3" pydantic-core = "2.10.1"
typing-extensions = ">=4.6.1" typing-extensions = ">=4.6.1"
[package.extras] [package.extras]
@ -2712,117 +2712,117 @@ email = ["email-validator (>=2.0.0)"]
[[package]] [[package]]
name = "pydantic-core" name = "pydantic-core"
version = "2.6.3" version = "2.10.1"
description = "" description = ""
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "pydantic_core-2.6.3-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:1a0ddaa723c48af27d19f27f1c73bdc615c73686d763388c8683fe34ae777bad"}, {file = "pydantic_core-2.10.1-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:d64728ee14e667ba27c66314b7d880b8eeb050e58ffc5fec3b7a109f8cddbd63"},
{file = "pydantic_core-2.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5cfde4fab34dd1e3a3f7f3db38182ab6c95e4ea91cf322242ee0be5c2f7e3d2f"}, {file = "pydantic_core-2.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:48525933fea744a3e7464c19bfede85df4aba79ce90c60b94d8b6e1eddd67096"},
{file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5493a7027bfc6b108e17c3383959485087d5942e87eb62bbac69829eae9bc1f7"}, {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef337945bbd76cce390d1b2496ccf9f90b1c1242a3a7bc242ca4a9fc5993427a"},
{file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:84e87c16f582f5c753b7f39a71bd6647255512191be2d2dbf49458c4ef024588"}, {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1392e0638af203cee360495fd2cfdd6054711f2db5175b6e9c3c461b76f5175"},
{file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:522a9c4a4d1924facce7270c84b5134c5cabcb01513213662a2e89cf28c1d309"}, {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0675ba5d22de54d07bccde38997e780044dcfa9a71aac9fd7d4d7a1d2e3e65f7"},
{file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaafc776e5edc72b3cad1ccedb5fd869cc5c9a591f1213aa9eba31a781be9ac1"}, {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:128552af70a64660f21cb0eb4876cbdadf1a1f9d5de820fed6421fa8de07c893"},
{file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a750a83b2728299ca12e003d73d1264ad0440f60f4fc9cee54acc489249b728"}, {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f6e6aed5818c264412ac0598b581a002a9f050cb2637a84979859e70197aa9e"},
{file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e8b374ef41ad5c461efb7a140ce4730661aadf85958b5c6a3e9cf4e040ff4bb"}, {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ecaac27da855b8d73f92123e5f03612b04c5632fd0a476e469dfc47cd37d6b2e"},
{file = "pydantic_core-2.6.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b594b64e8568cf09ee5c9501ede37066b9fc41d83d58f55b9952e32141256acd"}, {file = "pydantic_core-2.10.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b3c01c2fb081fced3bbb3da78510693dc7121bb893a1f0f5f4b48013201f362e"},
{file = "pydantic_core-2.6.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2a20c533cb80466c1d42a43a4521669ccad7cf2967830ac62c2c2f9cece63e7e"}, {file = "pydantic_core-2.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:92f675fefa977625105708492850bcbc1182bfc3e997f8eecb866d1927c98ae6"},
{file = "pydantic_core-2.6.3-cp310-none-win32.whl", hash = "sha256:04fe5c0a43dec39aedba0ec9579001061d4653a9b53a1366b113aca4a3c05ca7"}, {file = "pydantic_core-2.10.1-cp310-none-win32.whl", hash = "sha256:420a692b547736a8d8703c39ea935ab5d8f0d2573f8f123b0a294e49a73f214b"},
{file = "pydantic_core-2.6.3-cp310-none-win_amd64.whl", hash = "sha256:6bf7d610ac8f0065a286002a23bcce241ea8248c71988bda538edcc90e0c39ad"}, {file = "pydantic_core-2.10.1-cp310-none-win_amd64.whl", hash = "sha256:0880e239827b4b5b3e2ce05e6b766a7414e5f5aedc4523be6b68cfbc7f61c5d0"},
{file = "pydantic_core-2.6.3-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:6bcc1ad776fffe25ea5c187a028991c031a00ff92d012ca1cc4714087e575973"}, {file = "pydantic_core-2.10.1-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:073d4a470b195d2b2245d0343569aac7e979d3a0dcce6c7d2af6d8a920ad0bea"},
{file = "pydantic_core-2.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:df14f6332834444b4a37685810216cc8fe1fe91f447332cd56294c984ecbff1c"}, {file = "pydantic_core-2.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:600d04a7b342363058b9190d4e929a8e2e715c5682a70cc37d5ded1e0dd370b4"},
{file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b7486d85293f7f0bbc39b34e1d8aa26210b450bbd3d245ec3d732864009819"}, {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39215d809470f4c8d1881758575b2abfb80174a9e8daf8f33b1d4379357e417c"},
{file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a892b5b1871b301ce20d40b037ffbe33d1407a39639c2b05356acfef5536d26a"}, {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eeb3d3d6b399ffe55f9a04e09e635554012f1980696d6b0aca3e6cf42a17a03b"},
{file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:883daa467865e5766931e07eb20f3e8152324f0adf52658f4d302242c12e2c32"}, {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a7902bf75779bc12ccfc508bfb7a4c47063f748ea3de87135d433a4cca7a2f"},
{file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4eb77df2964b64ba190eee00b2312a1fd7a862af8918ec70fc2d6308f76ac64"}, {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3625578b6010c65964d177626fde80cf60d7f2e297d56b925cb5cdeda6e9925a"},
{file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce8c84051fa292a5dc54018a40e2a1926fd17980a9422c973e3ebea017aa8da"}, {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:caa48fc31fc7243e50188197b5f0c4228956f97b954f76da157aae7f67269ae8"},
{file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:22134a4453bd59b7d1e895c455fe277af9d9d9fbbcb9dc3f4a97b8693e7e2c9b"}, {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:07ec6d7d929ae9c68f716195ce15e745b3e8fa122fc67698ac6498d802ed0fa4"},
{file = "pydantic_core-2.6.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:02e1c385095efbd997311d85c6021d32369675c09bcbfff3b69d84e59dc103f6"}, {file = "pydantic_core-2.10.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e6f31a17acede6a8cd1ae2d123ce04d8cca74056c9d456075f4f6f85de055607"},
{file = "pydantic_core-2.6.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d79f1f2f7ebdb9b741296b69049ff44aedd95976bfee38eb4848820628a99b50"}, {file = "pydantic_core-2.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d8f1ebca515a03e5654f88411420fea6380fc841d1bea08effb28184e3d4899f"},
{file = "pydantic_core-2.6.3-cp311-none-win32.whl", hash = "sha256:430ddd965ffd068dd70ef4e4d74f2c489c3a313adc28e829dd7262cc0d2dd1e8"}, {file = "pydantic_core-2.10.1-cp311-none-win32.whl", hash = "sha256:6db2eb9654a85ada248afa5a6db5ff1cf0f7b16043a6b070adc4a5be68c716d6"},
{file = "pydantic_core-2.6.3-cp311-none-win_amd64.whl", hash = "sha256:84f8bb34fe76c68c9d96b77c60cef093f5e660ef8e43a6cbfcd991017d375950"}, {file = "pydantic_core-2.10.1-cp311-none-win_amd64.whl", hash = "sha256:4a5be350f922430997f240d25f8219f93b0c81e15f7b30b868b2fddfc2d05f27"},
{file = "pydantic_core-2.6.3-cp311-none-win_arm64.whl", hash = "sha256:5a2a3c9ef904dcdadb550eedf3291ec3f229431b0084666e2c2aa8ff99a103a2"}, {file = "pydantic_core-2.10.1-cp311-none-win_arm64.whl", hash = "sha256:5fdb39f67c779b183b0c853cd6b45f7db84b84e0571b3ef1c89cdb1dfc367325"},
{file = "pydantic_core-2.6.3-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:8421cf496e746cf8d6b677502ed9a0d1e4e956586cd8b221e1312e0841c002d5"}, {file = "pydantic_core-2.10.1-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:b1f22a9ab44de5f082216270552aa54259db20189e68fc12484873d926426921"},
{file = "pydantic_core-2.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bb128c30cf1df0ab78166ded1ecf876620fb9aac84d2413e8ea1594b588c735d"}, {file = "pydantic_core-2.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8572cadbf4cfa95fb4187775b5ade2eaa93511f07947b38f4cd67cf10783b118"},
{file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37a822f630712817b6ecc09ccc378192ef5ff12e2c9bae97eb5968a6cdf3b862"}, {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db9a28c063c7c00844ae42a80203eb6d2d6bbb97070cfa00194dff40e6f545ab"},
{file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:240a015102a0c0cc8114f1cba6444499a8a4d0333e178bc504a5c2196defd456"}, {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e2a35baa428181cb2270a15864ec6286822d3576f2ed0f4cd7f0c1708472aff"},
{file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f90e5e3afb11268628c89f378f7a1ea3f2fe502a28af4192e30a6cdea1e7d5e"}, {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05560ab976012bf40f25d5225a58bfa649bb897b87192a36c6fef1ab132540d7"},
{file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:340e96c08de1069f3d022a85c2a8c63529fd88709468373b418f4cf2c949fb0e"}, {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6495008733c7521a89422d7a68efa0a0122c99a5861f06020ef5b1f51f9ba7c"},
{file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1480fa4682e8202b560dcdc9eeec1005f62a15742b813c88cdc01d44e85308e5"}, {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ac492c686defc8e6133e3a2d9eaf5261b3df26b8ae97450c1647286750b901"},
{file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f14546403c2a1d11a130b537dda28f07eb6c1805a43dae4617448074fd49c282"}, {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8282bab177a9a3081fd3d0a0175a07a1e2bfb7fcbbd949519ea0980f8a07144d"},
{file = "pydantic_core-2.6.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a87c54e72aa2ef30189dc74427421e074ab4561cf2bf314589f6af5b37f45e6d"}, {file = "pydantic_core-2.10.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:aafdb89fdeb5fe165043896817eccd6434aee124d5ee9b354f92cd574ba5e78f"},
{file = "pydantic_core-2.6.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f93255b3e4d64785554e544c1c76cd32f4a354fa79e2eeca5d16ac2e7fdd57aa"}, {file = "pydantic_core-2.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f6defd966ca3b187ec6c366604e9296f585021d922e666b99c47e78738b5666c"},
{file = "pydantic_core-2.6.3-cp312-none-win32.whl", hash = "sha256:f70dc00a91311a1aea124e5f64569ea44c011b58433981313202c46bccbec0e1"}, {file = "pydantic_core-2.10.1-cp312-none-win32.whl", hash = "sha256:7c4d1894fe112b0864c1fa75dffa045720a194b227bed12f4be7f6045b25209f"},
{file = "pydantic_core-2.6.3-cp312-none-win_amd64.whl", hash = "sha256:23470a23614c701b37252618e7851e595060a96a23016f9a084f3f92f5ed5881"}, {file = "pydantic_core-2.10.1-cp312-none-win_amd64.whl", hash = "sha256:5994985da903d0b8a08e4935c46ed8daf5be1cf217489e673910951dc533d430"},
{file = "pydantic_core-2.6.3-cp312-none-win_arm64.whl", hash = "sha256:1ac1750df1b4339b543531ce793b8fd5c16660a95d13aecaab26b44ce11775e9"}, {file = "pydantic_core-2.10.1-cp312-none-win_arm64.whl", hash = "sha256:0d8a8adef23d86d8eceed3e32e9cca8879c7481c183f84ed1a8edc7df073af94"},
{file = "pydantic_core-2.6.3-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:a53e3195f134bde03620d87a7e2b2f2046e0e5a8195e66d0f244d6d5b2f6d31b"}, {file = "pydantic_core-2.10.1-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9badf8d45171d92387410b04639d73811b785b5161ecadabf056ea14d62d4ede"},
{file = "pydantic_core-2.6.3-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:f2969e8f72c6236c51f91fbb79c33821d12a811e2a94b7aa59c65f8dbdfad34a"}, {file = "pydantic_core-2.10.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:ebedb45b9feb7258fac0a268a3f6bec0a2ea4d9558f3d6f813f02ff3a6dc6698"},
{file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:672174480a85386dd2e681cadd7d951471ad0bb028ed744c895f11f9d51b9ebe"}, {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfe1090245c078720d250d19cb05d67e21a9cd7c257698ef139bc41cf6c27b4f"},
{file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:002d0ea50e17ed982c2d65b480bd975fc41086a5a2f9c924ef8fc54419d1dea3"}, {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e357571bb0efd65fd55f18db0a2fb0ed89d0bb1d41d906b138f088933ae618bb"},
{file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ccc13afee44b9006a73d2046068d4df96dc5b333bf3509d9a06d1b42db6d8bf"}, {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3dcd587b69bbf54fc04ca157c2323b8911033e827fffaecf0cafa5a892a0904"},
{file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:439a0de139556745ae53f9cc9668c6c2053444af940d3ef3ecad95b079bc9987"}, {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c120c9ce3b163b985a3b966bb701114beb1da4b0468b9b236fc754783d85aa3"},
{file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d63b7545d489422d417a0cae6f9898618669608750fc5e62156957e609e728a5"}, {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15d6bca84ffc966cc9976b09a18cf9543ed4d4ecbd97e7086f9ce9327ea48891"},
{file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b44c42edc07a50a081672e25dfe6022554b47f91e793066a7b601ca290f71e42"}, {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cabb9710f09d5d2e9e2748c3e3e20d991a4c5f96ed8f1132518f54ab2967221"},
{file = "pydantic_core-2.6.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1c721bfc575d57305dd922e6a40a8fe3f762905851d694245807a351ad255c58"}, {file = "pydantic_core-2.10.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:82f55187a5bebae7d81d35b1e9aaea5e169d44819789837cdd4720d768c55d15"},
{file = "pydantic_core-2.6.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5e4a2cf8c4543f37f5dc881de6c190de08096c53986381daebb56a355be5dfe6"}, {file = "pydantic_core-2.10.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1d40f55222b233e98e3921df7811c27567f0e1a4411b93d4c5c0f4ce131bc42f"},
{file = "pydantic_core-2.6.3-cp37-none-win32.whl", hash = "sha256:d9b4916b21931b08096efed090327f8fe78e09ae8f5ad44e07f5c72a7eedb51b"}, {file = "pydantic_core-2.10.1-cp37-none-win32.whl", hash = "sha256:14e09ff0b8fe6e46b93d36a878f6e4a3a98ba5303c76bb8e716f4878a3bee92c"},
{file = "pydantic_core-2.6.3-cp37-none-win_amd64.whl", hash = "sha256:a8acc9dedd304da161eb071cc7ff1326aa5b66aadec9622b2574ad3ffe225525"}, {file = "pydantic_core-2.10.1-cp37-none-win_amd64.whl", hash = "sha256:1396e81b83516b9d5c9e26a924fa69164156c148c717131f54f586485ac3c15e"},
{file = "pydantic_core-2.6.3-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:5e9c068f36b9f396399d43bfb6defd4cc99c36215f6ff33ac8b9c14ba15bdf6b"}, {file = "pydantic_core-2.10.1-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:6835451b57c1b467b95ffb03a38bb75b52fb4dc2762bb1d9dbed8de31ea7d0fc"},
{file = "pydantic_core-2.6.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e61eae9b31799c32c5f9b7be906be3380e699e74b2db26c227c50a5fc7988698"}, {file = "pydantic_core-2.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b00bc4619f60c853556b35f83731bd817f989cba3e97dc792bb8c97941b8053a"},
{file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85463560c67fc65cd86153a4975d0b720b6d7725cf7ee0b2d291288433fc21b"}, {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fa467fd300a6f046bdb248d40cd015b21b7576c168a6bb20aa22e595c8ffcdd"},
{file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9616567800bdc83ce136e5847d41008a1d602213d024207b0ff6cab6753fe645"}, {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d99277877daf2efe074eae6338453a4ed54a2d93fb4678ddfe1209a0c93a2468"},
{file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e9b65a55bbabda7fccd3500192a79f6e474d8d36e78d1685496aad5f9dbd92c"}, {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa7db7558607afeccb33c0e4bf1c9a9a835e26599e76af6fe2fcea45904083a6"},
{file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f468d520f47807d1eb5d27648393519655eadc578d5dd862d06873cce04c4d1b"}, {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aad7bd686363d1ce4ee930ad39f14e1673248373f4a9d74d2b9554f06199fb58"},
{file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9680dd23055dd874173a3a63a44e7f5a13885a4cfd7e84814be71be24fba83db"}, {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:443fed67d33aa85357464f297e3d26e570267d1af6fef1c21ca50921d2976302"},
{file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a718d56c4d55efcfc63f680f207c9f19c8376e5a8a67773535e6f7e80e93170"}, {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:042462d8d6ba707fd3ce9649e7bf268633a41018d6a998fb5fbacb7e928a183e"},
{file = "pydantic_core-2.6.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8ecbac050856eb6c3046dea655b39216597e373aa8e50e134c0e202f9c47efec"}, {file = "pydantic_core-2.10.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ecdbde46235f3d560b18be0cb706c8e8ad1b965e5c13bbba7450c86064e96561"},
{file = "pydantic_core-2.6.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:788be9844a6e5c4612b74512a76b2153f1877cd845410d756841f6c3420230eb"}, {file = "pydantic_core-2.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ed550ed05540c03f0e69e6d74ad58d026de61b9eaebebbaaf8873e585cbb18de"},
{file = "pydantic_core-2.6.3-cp38-none-win32.whl", hash = "sha256:07a1aec07333bf5adebd8264047d3dc518563d92aca6f2f5b36f505132399efc"}, {file = "pydantic_core-2.10.1-cp38-none-win32.whl", hash = "sha256:8cdbbd92154db2fec4ec973d45c565e767ddc20aa6dbaf50142676484cbff8ee"},
{file = "pydantic_core-2.6.3-cp38-none-win_amd64.whl", hash = "sha256:621afe25cc2b3c4ba05fff53525156d5100eb35c6e5a7cf31d66cc9e1963e378"}, {file = "pydantic_core-2.10.1-cp38-none-win_amd64.whl", hash = "sha256:9f6f3e2598604956480f6c8aa24a3384dbf6509fe995d97f6ca6103bb8c2534e"},
{file = "pydantic_core-2.6.3-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:813aab5bfb19c98ae370952b6f7190f1e28e565909bfc219a0909db168783465"}, {file = "pydantic_core-2.10.1-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:655f8f4c8d6a5963c9a0687793da37b9b681d9ad06f29438a3b2326d4e6b7970"},
{file = "pydantic_core-2.6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:50555ba3cb58f9861b7a48c493636b996a617db1a72c18da4d7f16d7b1b9952b"}, {file = "pydantic_core-2.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e570ffeb2170e116a5b17e83f19911020ac79d19c96f320cbfa1fa96b470185b"},
{file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19e20f8baedd7d987bd3f8005c146e6bcbda7cdeefc36fad50c66adb2dd2da48"}, {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64322bfa13e44c6c30c518729ef08fda6026b96d5c0be724b3c4ae4da939f875"},
{file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b0a5d7edb76c1c57b95df719af703e796fc8e796447a1da939f97bfa8a918d60"}, {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:485a91abe3a07c3a8d1e082ba29254eea3e2bb13cbbd4351ea4e5a21912cc9b0"},
{file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f06e21ad0b504658a3a9edd3d8530e8cea5723f6ea5d280e8db8efc625b47e49"}, {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7c2b8eb9fc872e68b46eeaf835e86bccc3a58ba57d0eedc109cbb14177be531"},
{file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea053cefa008fda40f92aab937fb9f183cf8752e41dbc7bc68917884454c6362"}, {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5cb87bdc2e5f620693148b5f8f842d293cae46c5f15a1b1bf7ceeed324a740c"},
{file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:171a4718860790f66d6c2eda1d95dd1edf64f864d2e9f9115840840cf5b5713f"}, {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25bd966103890ccfa028841a8f30cebcf5875eeac8c4bde4fe221364c92f0c9a"},
{file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ed7ceca6aba5331ece96c0e328cd52f0dcf942b8895a1ed2642de50800b79d3"}, {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f323306d0556351735b54acbf82904fe30a27b6a7147153cbe6e19aaaa2aa429"},
{file = "pydantic_core-2.6.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:acafc4368b289a9f291e204d2c4c75908557d4f36bd3ae937914d4529bf62a76"}, {file = "pydantic_core-2.10.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0c27f38dc4fbf07b358b2bc90edf35e82d1703e22ff2efa4af4ad5de1b3833e7"},
{file = "pydantic_core-2.6.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1aa712ba150d5105814e53cb141412217146fedc22621e9acff9236d77d2a5ef"}, {file = "pydantic_core-2.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f1365e032a477c1430cfe0cf2856679529a2331426f8081172c4a74186f1d595"},
{file = "pydantic_core-2.6.3-cp39-none-win32.whl", hash = "sha256:44b4f937b992394a2e81a5c5ce716f3dcc1237281e81b80c748b2da6dd5cf29a"}, {file = "pydantic_core-2.10.1-cp39-none-win32.whl", hash = "sha256:a1c311fd06ab3b10805abb72109f01a134019739bd3286b8ae1bc2fc4e50c07a"},
{file = "pydantic_core-2.6.3-cp39-none-win_amd64.whl", hash = "sha256:9b33bf9658cb29ac1a517c11e865112316d09687d767d7a0e4a63d5c640d1b17"}, {file = "pydantic_core-2.10.1-cp39-none-win_amd64.whl", hash = "sha256:ae8a8843b11dc0b03b57b52793e391f0122e740de3df1474814c700d2622950a"},
{file = "pydantic_core-2.6.3-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d7050899026e708fb185e174c63ebc2c4ee7a0c17b0a96ebc50e1f76a231c057"}, {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d43002441932f9a9ea5d6f9efaa2e21458221a3a4b417a14027a1d530201ef1b"},
{file = "pydantic_core-2.6.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:99faba727727b2e59129c59542284efebbddade4f0ae6a29c8b8d3e1f437beb7"}, {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fcb83175cc4936a5425dde3356f079ae03c0802bbdf8ff82c035f8a54b333521"},
{file = "pydantic_core-2.6.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fa159b902d22b283b680ef52b532b29554ea2a7fc39bf354064751369e9dbd7"}, {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:962ed72424bf1f72334e2f1e61b68f16c0e596f024ca7ac5daf229f7c26e4208"},
{file = "pydantic_core-2.6.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:046af9cfb5384f3684eeb3f58a48698ddab8dd870b4b3f67f825353a14441418"}, {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2cf5bb4dd67f20f3bbc1209ef572a259027c49e5ff694fa56bed62959b41e1f9"},
{file = "pydantic_core-2.6.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:930bfe73e665ebce3f0da2c6d64455098aaa67e1a00323c74dc752627879fc67"}, {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e544246b859f17373bed915182ab841b80849ed9cf23f1f07b73b7c58baee5fb"},
{file = "pydantic_core-2.6.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:85cc4d105747d2aa3c5cf3e37dac50141bff779545ba59a095f4a96b0a460e70"}, {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c0877239307b7e69d025b73774e88e86ce82f6ba6adf98f41069d5b0b78bd1bf"},
{file = "pydantic_core-2.6.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b25afe9d5c4f60dcbbe2b277a79be114e2e65a16598db8abee2a2dcde24f162b"}, {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:53df009d1e1ba40f696f8995683e067e3967101d4bb4ea6f667931b7d4a01357"},
{file = "pydantic_core-2.6.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e49ce7dc9f925e1fb010fc3d555250139df61fa6e5a0a95ce356329602c11ea9"}, {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a1254357f7e4c82e77c348dabf2d55f1d14d19d91ff025004775e70a6ef40ada"},
{file = "pydantic_core-2.6.3-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:2dd50d6a1aef0426a1d0199190c6c43ec89812b1f409e7fe44cb0fbf6dfa733c"}, {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:524ff0ca3baea164d6d93a32c58ac79eca9f6cf713586fdc0adb66a8cdeab96a"},
{file = "pydantic_core-2.6.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6595b0d8c8711e8e1dc389d52648b923b809f68ac1c6f0baa525c6440aa0daa"}, {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f0ac9fb8608dbc6eaf17956bf623c9119b4db7dbb511650910a82e261e6600f"},
{file = "pydantic_core-2.6.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ef724a059396751aef71e847178d66ad7fc3fc969a1a40c29f5aac1aa5f8784"}, {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:320f14bd4542a04ab23747ff2c8a778bde727158b606e2661349557f0770711e"},
{file = "pydantic_core-2.6.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3c8945a105f1589ce8a693753b908815e0748f6279959a4530f6742e1994dcb6"}, {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:63974d168b6233b4ed6a0046296803cb13c56637a7b8106564ab575926572a55"},
{file = "pydantic_core-2.6.3-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c8c6660089a25d45333cb9db56bb9e347241a6d7509838dbbd1931d0e19dbc7f"}, {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:417243bf599ba1f1fef2bb8c543ceb918676954734e2dcb82bf162ae9d7bd514"},
{file = "pydantic_core-2.6.3-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:692b4ff5c4e828a38716cfa92667661a39886e71136c97b7dac26edef18767f7"}, {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dda81e5ec82485155a19d9624cfcca9be88a405e2857354e5b089c2a982144b2"},
{file = "pydantic_core-2.6.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:f1a5d8f18877474c80b7711d870db0eeef9442691fcdb00adabfc97e183ee0b0"}, {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:14cfbb00959259e15d684505263d5a21732b31248a5dd4941f73a3be233865b9"},
{file = "pydantic_core-2.6.3-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:3796a6152c545339d3b1652183e786df648ecdf7c4f9347e1d30e6750907f5bb"}, {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:631cb7415225954fdcc2a024119101946793e5923f6c4d73a5914d27eb3d3a05"},
{file = "pydantic_core-2.6.3-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b962700962f6e7a6bd77e5f37320cabac24b4c0f76afeac05e9f93cf0c620014"}, {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:bec7dd208a4182e99c5b6c501ce0b1f49de2802448d4056091f8e630b28e9a52"},
{file = "pydantic_core-2.6.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56ea80269077003eaa59723bac1d8bacd2cd15ae30456f2890811efc1e3d4413"}, {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:149b8a07712f45b332faee1a2258d8ef1fb4a36f88c0c17cb687f205c5dc6e7d"},
{file = "pydantic_core-2.6.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c0ebbebae71ed1e385f7dfd9b74c1cff09fed24a6df43d326dd7f12339ec34"}, {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d966c47f9dd73c2d32a809d2be529112d509321c5310ebf54076812e6ecd884"},
{file = "pydantic_core-2.6.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:252851b38bad3bfda47b104ffd077d4f9604a10cb06fe09d020016a25107bf98"}, {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7eb037106f5c6b3b0b864ad226b0b7ab58157124161d48e4b30c4a43fef8bc4b"},
{file = "pydantic_core-2.6.3-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:6656a0ae383d8cd7cc94e91de4e526407b3726049ce8d7939049cbfa426518c8"}, {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:154ea7c52e32dce13065dbb20a4a6f0cc012b4f667ac90d648d36b12007fa9f7"},
{file = "pydantic_core-2.6.3-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d9140ded382a5b04a1c030b593ed9bf3088243a0a8b7fa9f071a5736498c5483"}, {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e562617a45b5a9da5be4abe72b971d4f00bf8555eb29bb91ec2ef2be348cd132"},
{file = "pydantic_core-2.6.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d38bbcef58220f9c81e42c255ef0bf99735d8f11edef69ab0b499da77105158a"}, {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:f23b55eb5464468f9e0e9a9935ce3ed2a870608d5f534025cd5536bca25b1402"},
{file = "pydantic_core-2.6.3-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:c9d469204abcca28926cbc28ce98f28e50e488767b084fb3fbdf21af11d3de26"}, {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:e9121b4009339b0f751955baf4543a0bfd6bc3f8188f8056b1a25a2d45099934"},
{file = "pydantic_core-2.6.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:48c1ed8b02ffea4d5c9c220eda27af02b8149fe58526359b3c07eb391cb353a2"}, {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:0523aeb76e03f753b58be33b26540880bac5aa54422e4462404c432230543f33"},
{file = "pydantic_core-2.6.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b2b1bfed698fa410ab81982f681f5b1996d3d994ae8073286515ac4d165c2e7"}, {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e0e2959ef5d5b8dc9ef21e1a305a21a36e254e6a34432d00c72a92fdc5ecda5"},
{file = "pydantic_core-2.6.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf9d42a71a4d7a7c1f14f629e5c30eac451a6fc81827d2beefd57d014c006c4a"}, {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da01bec0a26befab4898ed83b362993c844b9a607a86add78604186297eb047e"},
{file = "pydantic_core-2.6.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4292ca56751aebbe63a84bbfc3b5717abb09b14d4b4442cc43fd7c49a1529efd"}, {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2e9072d71c1f6cfc79a36d4484c82823c560e6f5599c43c1ca6b5cdbd54f881"},
{file = "pydantic_core-2.6.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7dc2ce039c7290b4ef64334ec7e6ca6494de6eecc81e21cb4f73b9b39991408c"}, {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f36a3489d9e28fe4b67be9992a23029c3cec0babc3bd9afb39f49844a8c721c5"},
{file = "pydantic_core-2.6.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:615a31b1629e12445c0e9fc8339b41aaa6cc60bd53bf802d5fe3d2c0cda2ae8d"}, {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f64f82cc3443149292b32387086d02a6c7fb39b8781563e0ca7b8d7d9cf72bd7"},
{file = "pydantic_core-2.6.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1fa1f6312fb84e8c281f32b39affe81984ccd484da6e9d65b3d18c202c666149"}, {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b4a6db486ac8e99ae696e09efc8b2b9fea67b63c8f88ba7a1a16c24a057a0776"},
{file = "pydantic_core-2.6.3.tar.gz", hash = "sha256:1508f37ba9e3ddc0189e6ff4e2228bd2d3c3a4641cbe8c07177162f76ed696c7"}, {file = "pydantic_core-2.10.1.tar.gz", hash = "sha256:0f8682dbdd2f67f8e1edddcbffcc29f60a6182b4901c367fc8c1c40d30bb0a82"},
] ]
[package.dependencies] [package.dependencies]
@ -2878,17 +2878,17 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
[[package]] [[package]]
name = "pylint" name = "pylint"
version = "2.17.5" version = "2.17.6"
description = "python code static checker" description = "python code static checker"
optional = false optional = false
python-versions = ">=3.7.2" python-versions = ">=3.7.2"
files = [ files = [
{file = "pylint-2.17.5-py3-none-any.whl", hash = "sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413"}, {file = "pylint-2.17.6-py3-none-any.whl", hash = "sha256:18a1412e873caf8ffb56b760ce1b5643675af23e6173a247b502406b24c716af"},
{file = "pylint-2.17.5.tar.gz", hash = "sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252"}, {file = "pylint-2.17.6.tar.gz", hash = "sha256:be928cce5c76bf9acdc65ad01447a1e0b1a7bccffc609fb7fc40f2513045bd05"},
] ]
[package.dependencies] [package.dependencies]
astroid = ">=2.15.6,<=2.17.0-dev0" astroid = ">=2.15.7,<=2.17.0-dev0"
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
dill = {version = ">=0.3.6", markers = "python_version >= \"3.11\""} dill = {version = ">=0.3.6", markers = "python_version >= \"3.11\""}
isort = ">=4.2.5,<6" isort = ">=4.2.5,<6"
@ -3422,39 +3422,39 @@ pyasn1 = ">=0.1.3"
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.0.290" version = "0.0.291"
description = "An extremely fast Python linter, written in Rust." description = "An extremely fast Python linter, written in Rust."
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "ruff-0.0.290-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:0e2b09ac4213b11a3520221083866a5816616f3ae9da123037b8ab275066fbac"}, {file = "ruff-0.0.291-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:b97d0d7c136a85badbc7fd8397fdbb336e9409b01c07027622f28dcd7db366f2"},
{file = "ruff-0.0.290-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:4ca6285aa77b3d966be32c9a3cd531655b3d4a0171e1f9bf26d66d0372186767"}, {file = "ruff-0.0.291-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:6ab44ea607967171e18aa5c80335237be12f3a1523375fa0cede83c5cf77feb4"},
{file = "ruff-0.0.290-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35e3550d1d9f2157b0fcc77670f7bb59154f223bff281766e61bdd1dd854e0c5"}, {file = "ruff-0.0.291-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a04b384f2d36f00d5fb55313d52a7d66236531195ef08157a09c4728090f2ef0"},
{file = "ruff-0.0.290-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d748c8bd97874f5751aed73e8dde379ce32d16338123d07c18b25c9a2796574a"}, {file = "ruff-0.0.291-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b727c219b43f903875b7503a76c86237a00d1a39579bb3e21ce027eec9534051"},
{file = "ruff-0.0.290-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:982af5ec67cecd099e2ef5e238650407fb40d56304910102d054c109f390bf3c"}, {file = "ruff-0.0.291-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87671e33175ae949702774071b35ed4937da06f11851af75cd087e1b5a488ac4"},
{file = "ruff-0.0.290-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:bbd37352cea4ee007c48a44c9bc45a21f7ba70a57edfe46842e346651e2b995a"}, {file = "ruff-0.0.291-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b75f5801547f79b7541d72a211949754c21dc0705c70eddf7f21c88a64de8b97"},
{file = "ruff-0.0.290-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d9be6351b7889462912e0b8185a260c0219c35dfd920fb490c7f256f1d8313e"}, {file = "ruff-0.0.291-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b09b94efdcd162fe32b472b2dd5bf1c969fcc15b8ff52f478b048f41d4590e09"},
{file = "ruff-0.0.290-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75cdc7fe32dcf33b7cec306707552dda54632ac29402775b9e212a3c16aad5e6"}, {file = "ruff-0.0.291-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d5b56bc3a2f83a7a1d7f4447c54d8d3db52021f726fdd55d549ca87bca5d747"},
{file = "ruff-0.0.290-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb07f37f7aecdbbc91d759c0c09870ce0fb3eed4025eebedf9c4b98c69abd527"}, {file = "ruff-0.0.291-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13f0d88e5f367b2dc8c7d90a8afdcfff9dd7d174e324fd3ed8e0b5cb5dc9b7f6"},
{file = "ruff-0.0.290-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2ab41bc0ba359d3f715fc7b705bdeef19c0461351306b70a4e247f836b9350ed"}, {file = "ruff-0.0.291-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b3eeee1b1a45a247758ecdc3ab26c307336d157aafc61edb98b825cadb153df3"},
{file = "ruff-0.0.290-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:150bf8050214cea5b990945b66433bf9a5e0cef395c9bc0f50569e7de7540c86"}, {file = "ruff-0.0.291-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6c06006350c3bb689765d71f810128c9cdf4a1121fd01afc655c87bab4fb4f83"},
{file = "ruff-0.0.290-py3-none-musllinux_1_2_i686.whl", hash = "sha256:75386ebc15fe5467248c039f5bf6a0cfe7bfc619ffbb8cd62406cd8811815fca"}, {file = "ruff-0.0.291-py3-none-musllinux_1_2_i686.whl", hash = "sha256:fd17220611047de247b635596e3174f3d7f2becf63bd56301fc758778df9b629"},
{file = "ruff-0.0.290-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ac93eadf07bc4ab4c48d8bb4e427bf0f58f3a9c578862eb85d99d704669f5da0"}, {file = "ruff-0.0.291-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5383ba67ad360caf6060d09012f1fb2ab8bd605ab766d10ca4427a28ab106e0b"},
{file = "ruff-0.0.290-py3-none-win32.whl", hash = "sha256:461fbd1fb9ca806d4e3d5c745a30e185f7cf3ca77293cdc17abb2f2a990ad3f7"}, {file = "ruff-0.0.291-py3-none-win32.whl", hash = "sha256:1d5f0616ae4cdc7a938b493b6a1a71c8a47d0300c0d65f6e41c281c2f7490ad3"},
{file = "ruff-0.0.290-py3-none-win_amd64.whl", hash = "sha256:f1f49f5ec967fd5778813780b12a5650ab0ebcb9ddcca28d642c689b36920796"}, {file = "ruff-0.0.291-py3-none-win_amd64.whl", hash = "sha256:8a69bfbde72db8ca1c43ee3570f59daad155196c3fbe357047cd9b77de65f15b"},
{file = "ruff-0.0.290-py3-none-win_arm64.whl", hash = "sha256:ae5a92dfbdf1f0c689433c223f8dac0782c2b2584bd502dfdbc76475669f1ba1"}, {file = "ruff-0.0.291-py3-none-win_arm64.whl", hash = "sha256:d867384a4615b7f30b223a849b52104214442b5ba79b473d7edd18da3cde22d6"},
{file = "ruff-0.0.290.tar.gz", hash = "sha256:949fecbc5467bb11b8db810a7fa53c7e02633856ee6bd1302b2f43adcd71b88d"}, {file = "ruff-0.0.291.tar.gz", hash = "sha256:c61109661dde9db73469d14a82b42a88c7164f731e6a3b0042e71394c1c7ceed"},
] ]
[[package]] [[package]]
name = "selenium" name = "selenium"
version = "4.12.0" version = "4.13.0"
description = "" description = ""
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "selenium-4.12.0-py3-none-any.whl", hash = "sha256:b2c48b1440db54a0653300d9955f5421390723d53b36ec835e18de8e13bbd401"}, {file = "selenium-4.13.0-py3-none-any.whl", hash = "sha256:f0f9185c01ae249a321529c4e3aa0edc2a900642e61fdbb76988cd72d2762ece"},
{file = "selenium-4.12.0.tar.gz", hash = "sha256:95be6aa449a0ab4ac1198bb9de71bbe9170405e04b9752f4b450dc7292a21828"}, {file = "selenium-4.13.0.tar.gz", hash = "sha256:3c413a4f1b8af67824703195e3b1c19cfb1c3186c799efa035d55fd59d6dd59f"},
] ]
[package.dependencies] [package.dependencies]

2176
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -42,8 +42,8 @@
"@open-wc/lit-helpers": "^0.6.0", "@open-wc/lit-helpers": "^0.6.0",
"@patternfly/elements": "^2.4.0", "@patternfly/elements": "^2.4.0",
"@patternfly/patternfly": "^4.224.2", "@patternfly/patternfly": "^4.224.2",
"@sentry/browser": "^7.70.0", "@sentry/browser": "^7.72.0",
"@sentry/tracing": "^7.70.0", "@sentry/tracing": "^7.72.0",
"@webcomponents/webcomponentsjs": "^2.8.0", "@webcomponents/webcomponentsjs": "^2.8.0",
"base64-js": "^1.5.1", "base64-js": "^1.5.1",
"chart.js": "^4.4.0", "chart.js": "^4.4.0",
@ -61,14 +61,14 @@
"yaml": "^2.3.2" "yaml": "^2.3.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.22.20", "@babel/core": "^7.23.0",
"@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-decorators": "^7.22.15", "@babel/plugin-proposal-decorators": "^7.23.0",
"@babel/plugin-transform-private-methods": "^7.22.5", "@babel/plugin-transform-private-methods": "^7.22.5",
"@babel/plugin-transform-private-property-in-object": "^7.22.11", "@babel/plugin-transform-private-property-in-object": "^7.22.11",
"@babel/plugin-transform-runtime": "^7.22.15", "@babel/plugin-transform-runtime": "^7.22.15",
"@babel/preset-env": "^7.22.20", "@babel/preset-env": "^7.22.20",
"@babel/preset-typescript": "^7.22.15", "@babel/preset-typescript": "^7.23.0",
"@hcaptcha/types": "^1.0.3", "@hcaptcha/types": "^1.0.3",
"@jackfranklin/rollup-plugin-markdown": "^0.4.0", "@jackfranklin/rollup-plugin-markdown": "^0.4.0",
"@jeysal/storybook-addon-css-user-preferences": "^0.2.0", "@jeysal/storybook-addon-css-user-preferences": "^0.2.0",
@ -78,22 +78,22 @@
"@rollup/plugin-node-resolve": "^15.2.1", "@rollup/plugin-node-resolve": "^15.2.1",
"@rollup/plugin-replace": "^5.0.2", "@rollup/plugin-replace": "^5.0.2",
"@rollup/plugin-terser": "^0.4.3", "@rollup/plugin-terser": "^0.4.3",
"@rollup/plugin-typescript": "^11.1.3", "@rollup/plugin-typescript": "^11.1.4",
"@storybook/addon-essentials": "^7.4.3", "@storybook/addon-essentials": "^7.4.5",
"@storybook/addon-links": "^7.4.3", "@storybook/addon-links": "^7.4.5",
"@storybook/blocks": "^7.1.1", "@storybook/blocks": "^7.1.1",
"@storybook/web-components": "^7.4.3", "@storybook/web-components": "^7.4.5",
"@storybook/web-components-vite": "^7.4.3", "@storybook/web-components-vite": "^7.4.5",
"@trivago/prettier-plugin-sort-imports": "^4.2.0", "@trivago/prettier-plugin-sort-imports": "^4.2.0",
"@types/chart.js": "^2.9.38", "@types/chart.js": "^2.9.38",
"@types/codemirror": "5.60.10", "@types/codemirror": "5.60.10",
"@types/grecaptcha": "^3.0.5", "@types/grecaptcha": "^3.0.5",
"@typescript-eslint/eslint-plugin": "^6.7.2", "@typescript-eslint/eslint-plugin": "^6.7.3",
"@typescript-eslint/parser": "^6.7.2", "@typescript-eslint/parser": "^6.7.3",
"babel-plugin-macros": "^3.1.0", "babel-plugin-macros": "^3.1.0",
"babel-plugin-tsconfig-paths": "^1.0.3", "babel-plugin-tsconfig-paths": "^1.0.3",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "^8.49.0", "eslint": "^8.50.0",
"eslint-config-google": "^0.14.0", "eslint-config-google": "^0.14.0",
"eslint-plugin-custom-elements": "0.0.8", "eslint-plugin-custom-elements": "0.0.8",
"eslint-plugin-lit": "^1.9.1", "eslint-plugin-lit": "^1.9.1",
@ -102,14 +102,14 @@
"lit-analyzer": "^1.2.1", "lit-analyzer": "^1.2.1",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"prettier": "^3.0.3", "prettier": "^3.0.3",
"pyright": "^1.1.328", "pyright": "^1.1.329",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"rollup": "^3.29.2", "rollup": "^3.29.4",
"rollup-plugin-copy": "^3.5.0", "rollup-plugin-copy": "^3.5.0",
"rollup-plugin-cssimport": "^1.0.3", "rollup-plugin-cssimport": "^1.0.3",
"rollup-plugin-postcss-lit": "^2.1.0", "rollup-plugin-postcss-lit": "^2.1.0",
"storybook": "^7.4.3", "storybook": "^7.4.5",
"storybook-addon-mock": "^4.3.0", "storybook-addon-mock": "^4.3.0",
"ts-lit-plugin": "^1.2.1", "ts-lit-plugin": "^1.2.1",
"tslib": "^2.6.2", "tslib": "^2.6.2",
@ -118,8 +118,8 @@
"vite-tsconfig-paths": "^4.2.1" "vite-tsconfig-paths": "^4.2.1"
}, },
"optionalDependencies": { "optionalDependencies": {
"@esbuild/darwin-arm64": "^0.19.3", "@esbuild/darwin-arm64": "^0.19.4",
"@esbuild/linux-amd64": "^0.18.11", "@esbuild/linux-amd64": "^0.18.11",
"@esbuild/linux-arm64": "^0.19.3" "@esbuild/linux-arm64": "^0.19.4"
} }
} }

View File

@ -62,7 +62,7 @@ export class InvitationStageForm extends ModelForm<InvitationStage, string> {
type="checkbox" type="checkbox"
?checked=${first( ?checked=${first(
this.instance?.continueFlowWithoutInvitation, this.instance?.continueFlowWithoutInvitation,
true, false,
)} )}
/> />
<span class="pf-c-switch__toggle"> <span class="pf-c-switch__toggle">

View File

@ -3,6 +3,7 @@ import "@goauthentik/admin/users/UserActiveForm";
import "@goauthentik/admin/users/UserForm"; import "@goauthentik/admin/users/UserForm";
import "@goauthentik/admin/users/UserPasswordForm"; import "@goauthentik/admin/users/UserPasswordForm";
import "@goauthentik/admin/users/UserResetEmailForm"; import "@goauthentik/admin/users/UserResetEmailForm";
import { me } from "@goauthentik/app/common/users";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { MessageLevel } from "@goauthentik/common/messages"; import { MessageLevel } from "@goauthentik/common/messages";
import { uiConfig } from "@goauthentik/common/ui/config"; import { uiConfig } from "@goauthentik/common/ui/config";
@ -37,6 +38,7 @@ import {
CoreUsersListTypeEnum, CoreUsersListTypeEnum,
Group, Group,
ResponseError, ResponseError,
SessionUser,
User, User,
} from "@goauthentik/api"; } from "@goauthentik/api";
@ -123,12 +125,15 @@ export class RelatedUserList extends Table<User> {
@property({ type: Boolean }) @property({ type: Boolean })
hideServiceAccounts = getURLParam<boolean>("hideServiceAccounts", true); hideServiceAccounts = getURLParam<boolean>("hideServiceAccounts", true);
@state()
me?: SessionUser;
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return super.styles.concat(PFDescriptionList, PFAlert, PFBanner); return super.styles.concat(PFDescriptionList, PFAlert, PFBanner);
} }
async apiEndpoint(page: number): Promise<PaginatedResponse<User>> { async apiEndpoint(page: number): Promise<PaginatedResponse<User>> {
return new CoreApi(DEFAULT_CONFIG).coreUsersList({ const users = await new CoreApi(DEFAULT_CONFIG).coreUsersList({
ordering: this.order, ordering: this.order,
page: page, page: page,
pageSize: (await uiConfig()).pagination.perPage, pageSize: (await uiConfig()).pagination.perPage,
@ -138,6 +143,8 @@ export class RelatedUserList extends Table<User> {
? [CoreUsersListTypeEnum.External, CoreUsersListTypeEnum.Internal] ? [CoreUsersListTypeEnum.External, CoreUsersListTypeEnum.Internal]
: undefined, : undefined,
}); });
this.me = await me();
return users;
} }
columns(): TableColumn[] { columns(): TableColumn[] {
@ -181,6 +188,9 @@ export class RelatedUserList extends Table<User> {
} }
row(item: User): TemplateResult[] { row(item: User): TemplateResult[] {
const canImpersonate =
rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate) &&
item.pk !== this.me?.user.pk;
return [ return [
html`<a href="#/identity/users/${item.pk}"> html`<a href="#/identity/users/${item.pk}">
<div>${item.username}</div> <div>${item.username}</div>
@ -200,7 +210,7 @@ export class RelatedUserList extends Table<User> {
</pf-tooltip> </pf-tooltip>
</button> </button>
</ak-forms-modal> </ak-forms-modal>
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate) ${canImpersonate
? html` ? html`
<ak-action-button <ak-action-button
class="pf-m-tertiary" class="pf-m-tertiary"

View File

@ -4,6 +4,7 @@ import "@goauthentik/admin/users/UserActiveForm";
import "@goauthentik/admin/users/UserForm"; import "@goauthentik/admin/users/UserForm";
import "@goauthentik/admin/users/UserPasswordForm"; import "@goauthentik/admin/users/UserPasswordForm";
import "@goauthentik/admin/users/UserResetEmailForm"; import "@goauthentik/admin/users/UserResetEmailForm";
import { me } from "@goauthentik/app/common/users";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { MessageLevel } from "@goauthentik/common/messages"; import { MessageLevel } from "@goauthentik/common/messages";
import { DefaultUIConfig, uiConfig } from "@goauthentik/common/ui/config"; import { DefaultUIConfig, uiConfig } from "@goauthentik/common/ui/config";
@ -30,7 +31,14 @@ import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
import PFCard from "@patternfly/patternfly/components/Card/card.css"; import PFCard from "@patternfly/patternfly/components/Card/card.css";
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css"; import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
import { CapabilitiesEnum, CoreApi, ResponseError, User, UserPath } from "@goauthentik/api"; import {
CapabilitiesEnum,
CoreApi,
ResponseError,
SessionUser,
User,
UserPath,
} from "@goauthentik/api";
@customElement("ak-user-list") @customElement("ak-user-list")
export class UserListPage extends TablePage<User> { export class UserListPage extends TablePage<User> {
@ -62,6 +70,9 @@ export class UserListPage extends TablePage<User> {
@state() @state()
userPaths?: UserPath; userPaths?: UserPath;
@state()
me?: SessionUser;
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return super.styles.concat(PFDescriptionList, PFCard, PFAlert); return super.styles.concat(PFDescriptionList, PFCard, PFAlert);
} }
@ -88,6 +99,7 @@ export class UserListPage extends TablePage<User> {
this.userPaths = await new CoreApi(DEFAULT_CONFIG).coreUsersPathsRetrieve({ this.userPaths = await new CoreApi(DEFAULT_CONFIG).coreUsersPathsRetrieve({
search: this.search, search: this.search,
}); });
this.me = await me();
return users; return users;
} }
@ -179,6 +191,9 @@ export class UserListPage extends TablePage<User> {
} }
row(item: User): TemplateResult[] { row(item: User): TemplateResult[] {
const canImpersonate =
rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate) &&
item.pk !== this.me?.user.pk;
return [ return [
html`<a href="#/identity/users/${item.pk}"> html`<a href="#/identity/users/${item.pk}">
<div>${item.username}</div> <div>${item.username}</div>
@ -198,7 +213,7 @@ export class UserListPage extends TablePage<User> {
</pf-tooltip> </pf-tooltip>
</button> </button>
</ak-forms-modal> </ak-forms-modal>
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate) ${canImpersonate
? html` ? html`
<ak-action-button <ak-action-button
class="pf-m-tertiary" class="pf-m-tertiary"

View File

@ -3,6 +3,7 @@ import "@goauthentik/admin/users/UserActiveForm";
import "@goauthentik/admin/users/UserChart"; import "@goauthentik/admin/users/UserChart";
import "@goauthentik/admin/users/UserForm"; import "@goauthentik/admin/users/UserForm";
import "@goauthentik/admin/users/UserPasswordForm"; import "@goauthentik/admin/users/UserPasswordForm";
import { me } from "@goauthentik/app/common/users";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { EVENT_REFRESH } from "@goauthentik/common/constants"; import { EVENT_REFRESH } from "@goauthentik/common/constants";
import { MessageLevel } from "@goauthentik/common/messages"; import { MessageLevel } from "@goauthentik/common/messages";
@ -24,7 +25,7 @@ import "@goauthentik/elements/user/UserConsentList";
import { msg, str } from "@lit/localize"; import { msg, str } from "@lit/localize";
import { CSSResult, TemplateResult, css, html } from "lit"; import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement, property } from "lit/decorators.js"; import { customElement, property, state } from "lit/decorators.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css"; import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFCard from "@patternfly/patternfly/components/Card/card.css"; import PFCard from "@patternfly/patternfly/components/Card/card.css";
@ -37,7 +38,7 @@ import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
import PFFlex from "@patternfly/patternfly/utilities/Flex/flex.css"; import PFFlex from "@patternfly/patternfly/utilities/Flex/flex.css";
import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css"; import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css";
import { CapabilitiesEnum, CoreApi, User } from "@goauthentik/api"; import { CapabilitiesEnum, CoreApi, SessionUser, User } from "@goauthentik/api";
import "./UserDevicesList"; import "./UserDevicesList";
@ -45,6 +46,8 @@ import "./UserDevicesList";
export class UserViewPage extends AKElement { export class UserViewPage extends AKElement {
@property({ type: Number }) @property({ type: Number })
set userId(id: number) { set userId(id: number) {
me().then((me) => {
this.me = me;
new CoreApi(DEFAULT_CONFIG) new CoreApi(DEFAULT_CONFIG)
.coreUsersRetrieve({ .coreUsersRetrieve({
id: id, id: id,
@ -52,11 +55,15 @@ export class UserViewPage extends AKElement {
.then((user) => { .then((user) => {
this.user = user; this.user = user;
}); });
});
} }
@property({ attribute: false }) @property({ attribute: false })
user?: User; user?: User;
@state()
me?: SessionUser;
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return [ return [
PFBase, PFBase,
@ -103,6 +110,9 @@ export class UserViewPage extends AKElement {
if (!this.user) { if (!this.user) {
return html``; return html``;
} }
const canImpersonate =
rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate) &&
this.user.pk !== this.me?.user.pk;
return html` return html`
<div class="pf-c-card__title">${msg("User Info")}</div> <div class="pf-c-card__title">${msg("User Info")}</div>
<div class="pf-c-card__body"> <div class="pf-c-card__body">
@ -213,9 +223,7 @@ export class UserViewPage extends AKElement {
</pf-tooltip> </pf-tooltip>
</button> </button>
</ak-user-active-form> </ak-user-active-form>
${rootInterface()?.config?.capabilities.includes( ${canImpersonate
CapabilitiesEnum.CanImpersonate,
)
? html` ? html`
<ak-action-button <ak-action-button
class="pf-m-secondary pf-m-block" class="pf-m-secondary pf-m-block"

View File

@ -1,5 +1,7 @@
import * as base64js from "base64-js"; import * as base64js from "base64-js";
import { msg } from "@lit/localize";
export function b64enc(buf: Uint8Array): string { export function b64enc(buf: Uint8Array): string {
return base64js.fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); return base64js.fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
} }
@ -14,6 +16,16 @@ export function u8arr(input: string): Uint8Array {
); );
} }
export function checkWebAuthnSupport() {
if ("credentials" in navigator) {
return;
}
if (window.location.protocol === "http:" && window.location.hostname !== "localhost") {
throw new Error(msg("WebAuthn requires this page to be accessed via HTTPS."));
}
throw new Error(msg("WebAuthn not supported by browser."));
}
/** /**
* Transforms items in the credentialCreateOptions generated on the server * Transforms items in the credentialCreateOptions generated on the server
* into byte arrays expected by the navigator.credentials.create() call * into byte arrays expected by the navigator.credentials.create() call

View File

@ -1,4 +1,5 @@
import { import {
checkWebAuthnSupport,
transformAssertionForServer, transformAssertionForServer,
transformCredentialRequestOptions, transformCredentialRequestOptions,
} from "@goauthentik/common/helpers/webauthn"; } from "@goauthentik/common/helpers/webauthn";
@ -57,6 +58,7 @@ export class AuthenticatorValidateStageWebAuthn extends BaseStage<
// request the authenticator to create an assertion signature using the // request the authenticator to create an assertion signature using the
// credential private key // credential private key
let assertion; let assertion;
checkWebAuthnSupport();
try { try {
assertion = await navigator.credentials.get({ assertion = await navigator.credentials.get({
publicKey: this.transformedCredentialRequestOptions, publicKey: this.transformedCredentialRequestOptions,

View File

@ -1,5 +1,6 @@
import { import {
Assertion, Assertion,
checkWebAuthnSupport,
transformCredentialCreateOptions, transformCredentialCreateOptions,
transformNewAssertionForServer, transformNewAssertionForServer,
} from "@goauthentik/common/helpers/webauthn"; } from "@goauthentik/common/helpers/webauthn";
@ -47,6 +48,7 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage<
if (!this.challenge) { if (!this.challenge) {
return; return;
} }
checkWebAuthnSupport();
// request the authenticator(s) to create a new credential keypair. // request the authenticator(s) to create a new credential keypair.
let credential; let credential;
try { try {

View File

@ -283,7 +283,7 @@ export class UserInterface extends Interface {
${this.me.user.isSuperuser ${this.me.user.isSuperuser
? html`<a ? html`<a
class="pf-c-button pf-m-secondary pf-m-small pf-u-display-none pf-u-display-block-on-md" class="pf-c-button pf-m-secondary pf-m-small pf-u-display-none pf-u-display-block-on-md"
href="/if/admin" href="/if/admin/"
> >
${msg("Admin interface")} ${msg("Admin interface")}
</a>` </a>`

View File

@ -9,7 +9,7 @@ import "@goauthentik/user/user-settings/details/stages/prompt/PromptStage";
import { msg } from "@lit/localize"; import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit"; import { CSSResult, TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js"; import { customElement, property, state } from "lit/decorators.js";
import { unsafeHTML } from "lit/directives/unsafe-html.js"; import { unsafeHTML } from "lit/directives/unsafe-html.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css"; import PFButton from "@patternfly/patternfly/components/Button/button.css";
@ -21,6 +21,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { import {
ChallengeChoices, ChallengeChoices,
ChallengeTypes, ChallengeTypes,
CurrentTenant,
FlowChallengeResponseRequest, FlowChallengeResponseRequest,
FlowErrorChallenge, FlowErrorChallenge,
FlowsApi, FlowsApi,
@ -34,6 +35,9 @@ export class UserSettingsFlowExecutor extends AKElement implements StageHost {
@property() @property()
flowSlug?: string; flowSlug?: string;
@state()
tenant?: CurrentTenant;
private _challenge?: ChallengeTypes; private _challenge?: ChallengeTypes;
@property({ attribute: false }) @property({ attribute: false })
@ -83,8 +87,8 @@ export class UserSettingsFlowExecutor extends AKElement implements StageHost {
} }
firstUpdated(): void { firstUpdated(): void {
const tenant = rootInterface()?.tenant; this.tenant = rootInterface()?.tenant;
this.flowSlug = tenant?.flowUserSettings; this.flowSlug = this.tenant?.flowUserSettings;
if (!this.flowSlug) { if (!this.flowSlug) {
return; return;
} }

View File

@ -5904,6 +5904,27 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sfb852dd507c25c24"> <trans-unit id="sfb852dd507c25c24">
<source>Discouraged: The authenticator should not create a dedicated credential</source> <source>Discouraged: The authenticator should not create a dedicated credential</source>
</trans-unit>
<trans-unit id="s028d385389b5aac0">
<source>Lock the user out of this system</source>
</trans-unit>
<trans-unit id="sd2122c514f0778b5">
<source>Allow the user to log in and use this system</source>
</trans-unit>
<trans-unit id="s43fe853bf219a9b8">
<source>Temporarily assume the identity of this user</source>
</trans-unit>
<trans-unit id="se28b5f3fcadaeeb1">
<source>Enter a new password for this user</source>
</trans-unit>
<trans-unit id="s6f5bb31e2733ecd5">
<source>Create a link for this user to reset their password</source>
</trans-unit>
<trans-unit id="s67ac11d47f1ce794">
<source>WebAuthn requires this page to be accessed via HTTPS.</source>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -6218,6 +6218,27 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sfb852dd507c25c24"> <trans-unit id="sfb852dd507c25c24">
<source>Discouraged: The authenticator should not create a dedicated credential</source> <source>Discouraged: The authenticator should not create a dedicated credential</source>
</trans-unit>
<trans-unit id="s028d385389b5aac0">
<source>Lock the user out of this system</source>
</trans-unit>
<trans-unit id="sd2122c514f0778b5">
<source>Allow the user to log in and use this system</source>
</trans-unit>
<trans-unit id="s43fe853bf219a9b8">
<source>Temporarily assume the identity of this user</source>
</trans-unit>
<trans-unit id="se28b5f3fcadaeeb1">
<source>Enter a new password for this user</source>
</trans-unit>
<trans-unit id="s6f5bb31e2733ecd5">
<source>Create a link for this user to reset their password</source>
</trans-unit>
<trans-unit id="s67ac11d47f1ce794">
<source>WebAuthn requires this page to be accessed via HTTPS.</source>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -5812,6 +5812,27 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sfb852dd507c25c24"> <trans-unit id="sfb852dd507c25c24">
<source>Discouraged: The authenticator should not create a dedicated credential</source> <source>Discouraged: The authenticator should not create a dedicated credential</source>
</trans-unit>
<trans-unit id="s028d385389b5aac0">
<source>Lock the user out of this system</source>
</trans-unit>
<trans-unit id="sd2122c514f0778b5">
<source>Allow the user to log in and use this system</source>
</trans-unit>
<trans-unit id="s43fe853bf219a9b8">
<source>Temporarily assume the identity of this user</source>
</trans-unit>
<trans-unit id="se28b5f3fcadaeeb1">
<source>Enter a new password for this user</source>
</trans-unit>
<trans-unit id="s6f5bb31e2733ecd5">
<source>Create a link for this user to reset their password</source>
</trans-unit>
<trans-unit id="s67ac11d47f1ce794">
<source>WebAuthn requires this page to be accessed via HTTPS.</source>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -5920,6 +5920,27 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sfb852dd507c25c24"> <trans-unit id="sfb852dd507c25c24">
<source>Discouraged: The authenticator should not create a dedicated credential</source> <source>Discouraged: The authenticator should not create a dedicated credential</source>
</trans-unit>
<trans-unit id="s028d385389b5aac0">
<source>Lock the user out of this system</source>
</trans-unit>
<trans-unit id="sd2122c514f0778b5">
<source>Allow the user to log in and use this system</source>
</trans-unit>
<trans-unit id="s43fe853bf219a9b8">
<source>Temporarily assume the identity of this user</source>
</trans-unit>
<trans-unit id="se28b5f3fcadaeeb1">
<source>Enter a new password for this user</source>
</trans-unit>
<trans-unit id="s6f5bb31e2733ecd5">
<source>Create a link for this user to reset their password</source>
</trans-unit>
<trans-unit id="s67ac11d47f1ce794">
<source>WebAuthn requires this page to be accessed via HTTPS.</source>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -6051,6 +6051,27 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sfb852dd507c25c24"> <trans-unit id="sfb852dd507c25c24">
<source>Discouraged: The authenticator should not create a dedicated credential</source> <source>Discouraged: The authenticator should not create a dedicated credential</source>
</trans-unit>
<trans-unit id="s028d385389b5aac0">
<source>Lock the user out of this system</source>
</trans-unit>
<trans-unit id="sd2122c514f0778b5">
<source>Allow the user to log in and use this system</source>
</trans-unit>
<trans-unit id="s43fe853bf219a9b8">
<source>Temporarily assume the identity of this user</source>
</trans-unit>
<trans-unit id="se28b5f3fcadaeeb1">
<source>Enter a new password for this user</source>
</trans-unit>
<trans-unit id="s6f5bb31e2733ecd5">
<source>Create a link for this user to reset their password</source>
</trans-unit>
<trans-unit id="s67ac11d47f1ce794">
<source>WebAuthn requires this page to be accessed via HTTPS.</source>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -6153,6 +6153,27 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sfb852dd507c25c24"> <trans-unit id="sfb852dd507c25c24">
<source>Discouraged: The authenticator should not create a dedicated credential</source> <source>Discouraged: The authenticator should not create a dedicated credential</source>
</trans-unit>
<trans-unit id="s028d385389b5aac0">
<source>Lock the user out of this system</source>
</trans-unit>
<trans-unit id="sd2122c514f0778b5">
<source>Allow the user to log in and use this system</source>
</trans-unit>
<trans-unit id="s43fe853bf219a9b8">
<source>Temporarily assume the identity of this user</source>
</trans-unit>
<trans-unit id="se28b5f3fcadaeeb1">
<source>Enter a new password for this user</source>
</trans-unit>
<trans-unit id="s6f5bb31e2733ecd5">
<source>Create a link for this user to reset their password</source>
</trans-unit>
<trans-unit id="s67ac11d47f1ce794">
<source>WebAuthn requires this page to be accessed via HTTPS.</source>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -5805,6 +5805,27 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sfb852dd507c25c24"> <trans-unit id="sfb852dd507c25c24">
<source>Discouraged: The authenticator should not create a dedicated credential</source> <source>Discouraged: The authenticator should not create a dedicated credential</source>
</trans-unit>
<trans-unit id="s028d385389b5aac0">
<source>Lock the user out of this system</source>
</trans-unit>
<trans-unit id="sd2122c514f0778b5">
<source>Allow the user to log in and use this system</source>
</trans-unit>
<trans-unit id="s43fe853bf219a9b8">
<source>Temporarily assume the identity of this user</source>
</trans-unit>
<trans-unit id="se28b5f3fcadaeeb1">
<source>Enter a new password for this user</source>
</trans-unit>
<trans-unit id="s6f5bb31e2733ecd5">
<source>Create a link for this user to reset their password</source>
</trans-unit>
<trans-unit id="s67ac11d47f1ce794">
<source>WebAuthn requires this page to be accessed via HTTPS.</source>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -7790,6 +7790,34 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sfb852dd507c25c24"> <trans-unit id="sfb852dd507c25c24">
<source>Discouraged: The authenticator should not create a dedicated credential</source> <source>Discouraged: The authenticator should not create a dedicated credential</source>
<target>避免:身份验证器不应该创建专用凭据</target> <target>避免:身份验证器不应该创建专用凭据</target>
</trans-unit>
<trans-unit id="s028d385389b5aac0">
<source>Lock the user out of this system</source>
<target>在此系统中锁定用户</target>
</trans-unit>
<trans-unit id="sd2122c514f0778b5">
<source>Allow the user to log in and use this system</source>
<target>允许用户登录并使用此系统</target>
</trans-unit>
<trans-unit id="s43fe853bf219a9b8">
<source>Temporarily assume the identity of this user</source>
<target>临时假定此用户的身份</target>
</trans-unit>
<trans-unit id="se28b5f3fcadaeeb1">
<source>Enter a new password for this user</source>
<target>为此用户输入新密码</target>
</trans-unit>
<trans-unit id="s6f5bb31e2733ecd5">
<source>Create a link for this user to reset their password</source>
<target>为此用户创建一个重置密码链接</target>
</trans-unit>
<trans-unit id="s67ac11d47f1ce794">
<source>WebAuthn requires this page to be accessed via HTTPS.</source>
<target>WebAuthn 需要此页面通过 HTTPS 访问。</target>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
<target>浏览器不支持 WebAuthn。</target>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -5857,6 +5857,27 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sfb852dd507c25c24"> <trans-unit id="sfb852dd507c25c24">
<source>Discouraged: The authenticator should not create a dedicated credential</source> <source>Discouraged: The authenticator should not create a dedicated credential</source>
</trans-unit>
<trans-unit id="s028d385389b5aac0">
<source>Lock the user out of this system</source>
</trans-unit>
<trans-unit id="sd2122c514f0778b5">
<source>Allow the user to log in and use this system</source>
</trans-unit>
<trans-unit id="s43fe853bf219a9b8">
<source>Temporarily assume the identity of this user</source>
</trans-unit>
<trans-unit id="se28b5f3fcadaeeb1">
<source>Enter a new password for this user</source>
</trans-unit>
<trans-unit id="s6f5bb31e2733ecd5">
<source>Create a link for this user to reset their password</source>
</trans-unit>
<trans-unit id="s67ac11d47f1ce794">
<source>WebAuthn requires this page to be accessed via HTTPS.</source>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -7790,6 +7790,34 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sfb852dd507c25c24"> <trans-unit id="sfb852dd507c25c24">
<source>Discouraged: The authenticator should not create a dedicated credential</source> <source>Discouraged: The authenticator should not create a dedicated credential</source>
<target>避免:身份验证器不应该创建专用凭据</target> <target>避免:身份验证器不应该创建专用凭据</target>
</trans-unit>
<trans-unit id="s028d385389b5aac0">
<source>Lock the user out of this system</source>
<target>在此系统中锁定用户</target>
</trans-unit>
<trans-unit id="sd2122c514f0778b5">
<source>Allow the user to log in and use this system</source>
<target>允许用户登录并使用此系统</target>
</trans-unit>
<trans-unit id="s43fe853bf219a9b8">
<source>Temporarily assume the identity of this user</source>
<target>临时假定此用户的身份</target>
</trans-unit>
<trans-unit id="se28b5f3fcadaeeb1">
<source>Enter a new password for this user</source>
<target>为此用户输入新密码</target>
</trans-unit>
<trans-unit id="s6f5bb31e2733ecd5">
<source>Create a link for this user to reset their password</source>
<target>为此用户创建一个重置密码链接</target>
</trans-unit>
<trans-unit id="s67ac11d47f1ce794">
<source>WebAuthn requires this page to be accessed via HTTPS.</source>
<target>WebAuthn 需要此页面通过 HTTPS 访问。</target>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
<target>浏览器不支持 WebAuthn。</target>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -5856,6 +5856,27 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit> </trans-unit>
<trans-unit id="sfb852dd507c25c24"> <trans-unit id="sfb852dd507c25c24">
<source>Discouraged: The authenticator should not create a dedicated credential</source> <source>Discouraged: The authenticator should not create a dedicated credential</source>
</trans-unit>
<trans-unit id="s028d385389b5aac0">
<source>Lock the user out of this system</source>
</trans-unit>
<trans-unit id="sd2122c514f0778b5">
<source>Allow the user to log in and use this system</source>
</trans-unit>
<trans-unit id="s43fe853bf219a9b8">
<source>Temporarily assume the identity of this user</source>
</trans-unit>
<trans-unit id="se28b5f3fcadaeeb1">
<source>Enter a new password for this user</source>
</trans-unit>
<trans-unit id="s6f5bb31e2733ecd5">
<source>Create a link for this user to reset their password</source>
</trans-unit>
<trans-unit id="s67ac11d47f1ce794">
<source>WebAuthn requires this page to be accessed via HTTPS.</source>
</trans-unit>
<trans-unit id="se9e9e1d6799b86a5">
<source>WebAuthn not supported by browser.</source>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>

View File

@ -25,7 +25,7 @@ image: ./image1.jpg
--- ---
Legacy security vendors that rely on black box development can't keep up with open source. It's an oft—discussed topic-the ability of open source communities to quickly jump in and collectively solve problems and innovate solutions—but it is equally believed that "serious" security software companies have proprietary software. Legacy security vendors that rely on black box development can't keep up with open source. It's an oft-discussed topic—the ability of open source communities to quickly jump in and collectively solve problems and innovate solutions—but it is equally believed that "serious" security software companies have proprietary software.
In this blog, we will take a closer look at the pros and cons of the various source availability types of SSO and other security software. In this blog, we will take a closer look at the pros and cons of the various source availability types of SSO and other security software.

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -0,0 +1,138 @@
---
title: "Machine-to-machine communication in authentik"
slug: 2023-09-26-machine-to-machine-communication-in-authentik
authors:
- name: Jens Langhammer
title: CTO at Authentik Security Inc
url: https://github.com/BeryJu
image_url: https://github.com/BeryJu.png
tags:
- machine-to-machine
- M2M
- SSO
- open source
- identity provider
- security
- authentication
- Docker
- Kubernetes
- Loki
hide_table_of_contents: false
image: ./Image1.png
---
> **_authentik is a unified identity platform that helps with all of your authentication needs, replacing Okta, Active Directory, Auth0, and more. Building on the open-source project, Authentik Security Inc is a [public benefit company](https://github.com/OpenCoreVentures/ocv-public-benefit-company/blob/main/ocv-public-benefit-company-charter.md) that provides additional features and dedicated support._**
---
We have provided M2M communication in authentik for the past year, and in this blog we want to share some more information about how it works in authentik, and take a look at three use cases.
## What is M2M?
Broadly speaking, M2M communication is the process by which machines (devices, laptops, servers, smart appliances, or more precisely the client interface of any thing that can be digitally communicated with) exchange data. Machine-to-machine communication is an important component of IoT, the Internet of Things; M2M is how all of the “things” communicate. So M2M is more about the communication between the devices, while IoT is the larger, more complex, overarching technology.
Interestingly, M2M is also implemented as a communication process between business systems, such as banking services, or payroll workflows. One of the first fields to heavily utilize M2M is the [oil and gas industry](https://blog.orbcomm.com/onshore-to-offshore-how-m2m-is-changing-oil-gas-world/); everything from monitoring the production (volume, pressure, etc.) of gas wells, to tracking fleets of trucks and sea vessels, to the health of pipelines can be done using M2M communication.
Financial systems, analytics, really any work that involves multi-machine data processing, can be optimized using M2M.
> “Machine to machine systems are the key to reliable data processing with near to zero errors” ([source](https://dataconomy.com/2023/07/14/what-is-machine-to-machine-m2m/))
Where there is communication in software systems, there is both authentication and authorization. The basic definition of the terms is that _authentication_ is about assessing and verifying WHO (the person, device, thing) is involved, while **_authorization_** is about what access rights that person or device has. So we choose to use the phrase “machine-to-machine communication” in order to capture both of those important aspects.
> Or we could use fun terms like **AuthN** (authentication) and **AuthZ** (authorization).
So in some ways you can think of M2M as being like an internal API, with data (tokens and keys and certs and all thing access-related) being passed back and forth, but specifically for authentication and authorization processes.
!["Screenshot of authentik UI"](./Image1.png)
<!--truncate-->
## M2M communication in authentik
As part of our providing a unified platform for authentication, authentik supports OAuth2-based M2M communication. By “unified platform” we mean that authentik provides workplace authentication for team members, B2C login by web site visitors, global communities and non-profit teams, educational societies, and [coming soon] mobile authentication. So that all authentications needs are met by authentik, as a unified platform.
### Use cases for M2M in authentik
Macine-to-machine communication speeds processing and adds a layer of security to inter-application and complex, multi-machine systems. With authentiks M2M functionality, you can take advantage of these aspects, and optimize your workflow for authentication and authorization between servers, applications, and any provider or source in your ecosystem.
**Common workflow**
The workflow for all three of the use cases that we discuss below share several core common steps:
1. Obtain a token from the environment you are working in (i.e. a build/CI tool such as GitLab or GitHub, or Kubernetes for applications running on Kubernetes).
2. Pass the token, via [client_credentials](https://goauthentik.io/docs/providers/oauth2/client_credentials), to authentik.
3. In the response, authentik returns a JWT (JSON Web Token).
4. The token is then used to authenticate requests to other services elsewhere. (These other services need to check the token for its validity, which can be done with the [proxy provider](https://goauthentik.io/docs/providers/proxy/) in authentik for example).
**Three authentik use cases**
Lets take a look at three specific use cases for implementing M2M with authentik.
**1. Building Docker images and passing them to a [Docker registry](https://docs.docker.com/registry/)**
After building and testing your application, you might want to package your application as a Docker image and push it to a registry so that others can use it for deployment.
For this use case, you can use M2M with authentik to push the package to your registry without needing to login yourself, or needing a password, or even a pre-defined service account, to the registry. Instead, you can create a policy with authentik to allow a specific repository in your CI platform to push to the Docker registry. When logging into the registry, you can use the token you already have access to from the platform youre running on, and the rest happens behind the scenes!
For a real-life example, with code samples, take a look at my blog “[Setup a docker registry for passwordless Docker builds with GitHub/GitLab using authentik](https://beryju.io/blog/2022-06-github-gitlab-passwordless-docker/)”, which provides step-by-step instructions with code blocks.
**2. Collect Prometheus metrics from multiple clusters**
If you use Prometheus to monitor multiple Kubernetes clusters, you might want to collect all Prometheus metrics and put them in one place, using something like [Thanos](https://thanos.io/) or [Mimir](https://grafana.com/oss/mimir/) in order to better analyze the data. Using M2M functionality in authentik, you can simplify authentication, so that the source (the cluster sending the metrics, in this case) can authenticate itself with the receiving target cluster.
In this use case, you will create an expression policy, in which you define service accounts to allow communication between that specific cluster and authentik.
- You create an OAuth Source for each cluster (since each cluster usually has its own unique JWT Signing key). On the **Create a new source** panel, select **OpenID OAuth Source** as the type, and then click **Next**. Then you will need to populate the following fields:
- **Consumer key**, **Consumer secret**, **Authorization URL**, **Access token URL**, and **Profile URL, and OIDC JWKS** (to obtain the key for the cluster, run the command `kubectl get --raw /openid/v1/jwks`).
- You can create a proxy provider to authenticate the incoming requests, where the proxy provider functions like a traditional reverse-proxy, sending traffic to Thanos or Mimir in the cluster but also requiring authentication for any requests. When defining your proxy provider, use the following syntax:
```python
# Replace these values with the namespace and service-account name for your prometheus instance
allowed_namespace = "prometheus-namespace"
allowed_service_account = "prometheus-sa"
jwt = request.context.get("oauth_jwt", None)
if not jwt:
return False
allowed_sa = [
f"system:serviceaccount:{allowed_namespace}:{allowed_service_account}",
]
return jwt["sub"] in allowed_sa
```
Then the rest is same as in the first use case; obtain a JWT from the K8s cluster, send the token to authentik, get back a diff token, then send that token to Thanos, Mimir, or where ever you want to store the metrics. Prometheus then uses that token to authenticate incoming requests from the other clusters. Actually, you can configure Promethesus to do the token exchange work, by using the `oauth2` configuration option. For an example of how this can be set up, refer to [this YAML file](https://github.com/BeryJu/k8s/blob/b4b26e5/common-monitoring/monitoring-system/prom-agent.yaml#L24-L39), where I configured `remote_write`.
**3. GitOps with M2M and Loki**
This third use case is a twist on the first two use cases, but even more simple.
We can utilize GitOps to configure [Loki alerting rules](https://grafana.com/docs/loki/latest/alert/), by using GitHub actions and a proxy provider to make Loki publicly accessible. This setup combines the use of a CI platform (as in the first use case) and using a proxy provider to authenticate requests (as in the second use case). In this third case, the authentication is for the requests from GitHub Actions to Loki.
- Create an OAuth Source for GitHub, selecting **OpenID OAuth Source** as the type. Then, instead of populating the **OIDC JWKS** field, you use the **OIDC JWKS URL** field and set that to https://token.actions.githubusercontent.com/.well-known/jwks.
- As with the second use case, create proxy provider, which acts like a traditional reverse-proxy, sending traffic to Loki, but also authenticating any requests.
- Create an expression policy, using the following syntax:
```python
# Replace the two values below
github_user = "my-user"
github_repo = "my-repo"
jwt = request.context.get("oauth_jwt", None)
if not jwt:
return False
if jwt["iss"] != "https://token.actions.githubusercontent.com":
return False
if jwt["repository"] != f"{github_user}/{github_repo}":
return False
return True
```
- Finally, call a snippet in a GitHub composite action (this can be done manually or programmatically) to exchange the tokens between the GitHub action and Loki. The proxy provider then verifies the tokens and forwards the requests to Loki.
### Whats next
Look for our upcoming tutorial about configuring machine-to-machine communication using authentik. As part of the tutorial, we will provide a GitHub composite action that bundles the multiple steps involved in token creation and exchange into a single, reusable action, instead of needing multiple `run` commands.
Wed like to hear from you about how you use M2M, or how you plan to in the future. And as always, if you are interested in collaborating with us on our M2M functionality, or contributing to our documentation, visit us in our [GitHub repository](https://github.com/goauthentik/authentik) or reach out to us at [hello@goauthentik.io](mailto:hello@goauthentik.io).

View File

@ -363,6 +363,16 @@ Configure how many gunicorn threads a worker processes should have (see https://
Defaults to 4. Defaults to 4.
### `AUTHENTIK_WORKER__CONCURRENCY`
:::info
Requires authentik 2023.9.0
:::
Configure Celery worker concurrency for authentik worker (see https://docs.celeryq.dev/en/latest/userguide/configuration.html#worker-concurrency). This essentially defines the number of worker processes spawned for a single worker.
Defaults to 2.
## Custom python settings ## Custom python settings
To modify additional settings further than the options above allow, you can create a custom python file and mount it to `/data/user_settings.py`. This file will be loaded on startup by both the server and the worker. All default settings are [here](https://github.com/goauthentik/authentik/blob/main/authentik/root/settings.py) To modify additional settings further than the options above allow, you can create a custom python file and mount it to `/data/user_settings.py`. This file will be loaded on startup by both the server and the worker. All default settings are [here](https://github.com/goauthentik/authentik/blob/main/authentik/root/settings.py)

View File

@ -10,7 +10,7 @@ Configure your monitoring software to send requests to `/-/health/live/`, which
## Worker monitoring ## Worker monitoring
The worker container can be monitored by running `/lifecycle/ak healthcheck` in the worker container. This will ping the worker and ensure it can communicate with redis as required. The worker container can be monitored by running `ak healthcheck` in the worker container. This will ping the worker and ensure it can communicate with redis as required.
## Outpost monitoring ## Outpost monitoring

View File

@ -3,6 +3,7 @@ const fs = require("fs").promises;
/** @type {import('@docusaurus/types').DocusaurusConfig} */ /** @type {import('@docusaurus/types').DocusaurusConfig} */
module.exports = async function () { module.exports = async function () {
const remarkGithub = (await import("remark-github")).default; const remarkGithub = (await import("remark-github")).default;
const defaultBuildUrl = (await import("remark-github")).defaultBuildUrl;
const footerEmail = await fs.readFile("src/footer.html", { const footerEmail = await fs.readFile("src/footer.html", {
encoding: "utf-8", encoding: "utf-8",
}); });
@ -137,10 +138,7 @@ module.exports = async function () {
{ {
repository: "goauthentik/authentik", repository: "goauthentik/authentik",
// Only replace issues and PR links // Only replace issues and PR links
buildUrl: function ( buildUrl: function (values) {
values,
defaultBuildUrl,
) {
return values.type === "issue" return values.type === "issue"
? defaultBuildUrl(values) ? defaultBuildUrl(values)
: false; : false;

View File

@ -2,6 +2,7 @@ const config = require("./docusaurus.config");
module.exports = async function () { module.exports = async function () {
const remarkGithub = (await import("remark-github")).default; const remarkGithub = (await import("remark-github")).default;
const defaultBuildUrl = (await import("remark-github")).defaultBuildUrl;
const mainConfig = await config(); const mainConfig = await config();
return { return {
title: "authentik", title: "authentik",
@ -71,10 +72,7 @@ module.exports = async function () {
{ {
repository: "goauthentik/authentik", repository: "goauthentik/authentik",
// Only replace issues and PR links // Only replace issues and PR links
buildUrl: function ( buildUrl: function (values) {
values,
defaultBuildUrl,
) {
return values.type === "issue" return values.type === "issue"
? defaultBuildUrl(values) ? defaultBuildUrl(values)
: false; : false;

View File

@ -15,15 +15,15 @@
"@mdx-js/react": "^1.6.22", "@mdx-js/react": "^1.6.22",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"disqus-react": "^1.1.5", "disqus-react": "^1.1.5",
"postcss": "^8.4.30", "postcss": "^8.4.31",
"rapidoc": "^9.3.4", "rapidoc": "^9.3.4",
"react": "^17.0.2", "react": "^17.0.2",
"react-before-after-slider-component": "^1.1.8", "react-before-after-slider-component": "^1.1.8",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-feather": "^2.0.10", "react-feather": "^2.0.10",
"react-toggle": "^4.1.3", "react-toggle": "^4.1.3",
"react-tooltip": "^5.21.4", "react-tooltip": "^5.21.5",
"remark-github": "^11.2.4" "remark-github": "^12.0.0"
}, },
"devDependencies": { "devDependencies": {
"prettier": "3.0.3" "prettier": "3.0.3"
@ -8503,20 +8503,33 @@
} }
}, },
"node_modules/mdast-util-find-and-replace": { "node_modules/mdast-util-find-and-replace": {
"version": "2.2.2", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.2.tgz", "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz",
"integrity": "sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==", "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==",
"dependencies": { "dependencies": {
"@types/mdast": "^3.0.0", "@types/mdast": "^4.0.0",
"escape-string-regexp": "^5.0.0", "escape-string-regexp": "^5.0.0",
"unist-util-is": "^5.0.0", "unist-util-is": "^6.0.0",
"unist-util-visit-parents": "^5.0.0" "unist-util-visit-parents": "^6.0.0"
}, },
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
"url": "https://opencollective.com/unified" "url": "https://opencollective.com/unified"
} }
}, },
"node_modules/mdast-util-find-and-replace/node_modules/@types/mdast": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.0.tgz",
"integrity": "sha512-YLeG8CujC9adtj/kuDzq1N4tCDYKoZ5l/bnjq8d74+t/3q/tHquJOJKUQXJrLCflOHpKjXgcI/a929gpmLOEng==",
"dependencies": {
"@types/unist": "*"
}
},
"node_modules/mdast-util-find-and-replace/node_modules/@types/unist": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.0.tgz",
"integrity": "sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w=="
},
"node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
@ -8529,11 +8542,11 @@
} }
}, },
"node_modules/mdast-util-find-and-replace/node_modules/unist-util-is": { "node_modules/mdast-util-find-and-replace/node_modules/unist-util-is": {
"version": "5.2.1", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
"integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
"dependencies": { "dependencies": {
"@types/unist": "^2.0.0" "@types/unist": "^3.0.0"
}, },
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@ -8541,12 +8554,12 @@
} }
}, },
"node_modules/mdast-util-find-and-replace/node_modules/unist-util-visit-parents": { "node_modules/mdast-util-find-and-replace/node_modules/unist-util-visit-parents": {
"version": "5.1.3", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
"integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
"dependencies": { "dependencies": {
"@types/unist": "^2.0.0", "@types/unist": "^3.0.0",
"unist-util-is": "^5.0.0" "unist-util-is": "^6.0.0"
}, },
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@ -9426,9 +9439,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.30", "version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
"integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -10611,9 +10624,9 @@
} }
}, },
"node_modules/react-tooltip": { "node_modules/react-tooltip": {
"version": "5.21.4", "version": "5.21.5",
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.21.4.tgz", "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.21.5.tgz",
"integrity": "sha512-LZsllEbiu63zNwuCalq3gIFcBu2Xf0I0fMg7uuF7/5ROo5//uHe8Sum7v9L1Rtp6IozcoU9YAjkNUZdrxutsNg==", "integrity": "sha512-ey70qf6pBGi4U6xpyNlZAHobAhlo2dfxmImR2Bzd/DbLTsAYWz3TEaK+RMFuUZMq6hSPRbUHQSkP2rHBq4uFVg==",
"dependencies": { "dependencies": {
"@floating-ui/dom": "^1.0.0", "@floating-ui/dom": "^1.0.0",
"classnames": "^2.3.0" "classnames": "^2.3.0"
@ -10788,74 +10801,41 @@
} }
}, },
"node_modules/remark-github": { "node_modules/remark-github": {
"version": "11.2.4", "version": "12.0.0",
"resolved": "https://registry.npmjs.org/remark-github/-/remark-github-11.2.4.tgz", "resolved": "https://registry.npmjs.org/remark-github/-/remark-github-12.0.0.tgz",
"integrity": "sha512-GJjWFpwqdrHHhPWqMbb8+lqFLiHQ9pCzUmXmRrhMFXGpYov5n2ljsZzuWgXlfzArfQYkiKIZczA2I8IHYMHqCA==", "integrity": "sha512-ByefQKFN184LeiGRCabfl7zUJsdlMYWEhiLX1gpmQ11yFg6xSuOTW7LVCv0oc1x+YvUMJW23NU36sJX2RWGgvg==",
"dependencies": { "dependencies": {
"@types/mdast": "^3.0.0", "@types/mdast": "^4.0.0",
"mdast-util-find-and-replace": "^2.0.0", "mdast-util-find-and-replace": "^3.0.0",
"mdast-util-to-string": "^3.0.0", "mdast-util-to-string": "^4.0.0",
"unified": "^10.0.0", "to-vfile": "^8.0.0",
"unist-util-visit": "^4.0.0" "unist-util-visit": "^5.0.0",
"vfile": "^6.0.0"
}, },
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
"url": "https://opencollective.com/unified" "url": "https://opencollective.com/unified"
} }
}, },
"node_modules/remark-github/node_modules/bail": { "node_modules/remark-github/node_modules/@types/mdast": {
"version": "2.0.2", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.0.tgz",
"integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", "integrity": "sha512-YLeG8CujC9adtj/kuDzq1N4tCDYKoZ5l/bnjq8d74+t/3q/tHquJOJKUQXJrLCflOHpKjXgcI/a929gpmLOEng==",
"funding": { "dependencies": {
"type": "github", "@types/unist": "*"
"url": "https://github.com/sponsors/wooorm"
} }
}, },
"node_modules/remark-github/node_modules/is-plain-obj": { "node_modules/remark-github/node_modules/@types/unist": {
"version": "4.1.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.0.tgz",
"integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "integrity": "sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w=="
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
}, },
"node_modules/remark-github/node_modules/mdast-util-to-string": { "node_modules/remark-github/node_modules/mdast-util-to-string": {
"version": "3.2.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
"integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
"dependencies": { "dependencies": {
"@types/mdast": "^3.0.0" "@types/mdast": "^4.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/remark-github/node_modules/trough": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz",
"integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/remark-github/node_modules/unified": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz",
"integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==",
"dependencies": {
"@types/unist": "^2.0.0",
"bail": "^2.0.0",
"extend": "^3.0.0",
"is-buffer": "^2.0.0",
"is-plain-obj": "^4.0.0",
"trough": "^2.0.0",
"vfile": "^5.0.0"
}, },
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@ -10863,11 +10843,11 @@
} }
}, },
"node_modules/remark-github/node_modules/unist-util-is": { "node_modules/remark-github/node_modules/unist-util-is": {
"version": "5.2.1", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
"integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
"dependencies": { "dependencies": {
"@types/unist": "^2.0.0" "@types/unist": "^3.0.0"
}, },
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@ -10875,11 +10855,11 @@
} }
}, },
"node_modules/remark-github/node_modules/unist-util-stringify-position": { "node_modules/remark-github/node_modules/unist-util-stringify-position": {
"version": "3.0.3", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
"integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
"dependencies": { "dependencies": {
"@types/unist": "^2.0.0" "@types/unist": "^3.0.0"
}, },
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@ -10887,13 +10867,13 @@
} }
}, },
"node_modules/remark-github/node_modules/unist-util-visit": { "node_modules/remark-github/node_modules/unist-util-visit": {
"version": "4.1.2", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
"integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
"dependencies": { "dependencies": {
"@types/unist": "^2.0.0", "@types/unist": "^3.0.0",
"unist-util-is": "^5.0.0", "unist-util-is": "^6.0.0",
"unist-util-visit-parents": "^5.1.1" "unist-util-visit-parents": "^6.0.0"
}, },
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@ -10901,12 +10881,12 @@
} }
}, },
"node_modules/remark-github/node_modules/unist-util-visit-parents": { "node_modules/remark-github/node_modules/unist-util-visit-parents": {
"version": "5.1.3", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
"integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
"dependencies": { "dependencies": {
"@types/unist": "^2.0.0", "@types/unist": "^3.0.0",
"unist-util-is": "^5.0.0" "unist-util-is": "^6.0.0"
}, },
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@ -10914,14 +10894,13 @@
} }
}, },
"node_modules/remark-github/node_modules/vfile": { "node_modules/remark-github/node_modules/vfile": {
"version": "5.3.7", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz",
"integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==",
"dependencies": { "dependencies": {
"@types/unist": "^2.0.0", "@types/unist": "^3.0.0",
"is-buffer": "^2.0.0", "unist-util-stringify-position": "^4.0.0",
"unist-util-stringify-position": "^3.0.0", "vfile-message": "^4.0.0"
"vfile-message": "^3.0.0"
}, },
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@ -10929,12 +10908,12 @@
} }
}, },
"node_modules/remark-github/node_modules/vfile-message": { "node_modules/remark-github/node_modules/vfile-message": {
"version": "3.1.4", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz",
"integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==",
"dependencies": { "dependencies": {
"@types/unist": "^2.0.0", "@types/unist": "^3.0.0",
"unist-util-stringify-position": "^3.0.0" "unist-util-stringify-position": "^4.0.0"
}, },
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@ -12373,6 +12352,62 @@
"node": ">=8.0" "node": ">=8.0"
} }
}, },
"node_modules/to-vfile": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/to-vfile/-/to-vfile-8.0.0.tgz",
"integrity": "sha512-IcmH1xB5576MJc9qcfEC/m/nQCFt3fzMHz45sSlgJyTWjRbKW1HAkJpuf3DgE57YzIlZcwcBZA5ENQbBo4aLkg==",
"dependencies": {
"vfile": "^6.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/to-vfile/node_modules/@types/unist": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.0.tgz",
"integrity": "sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w=="
},
"node_modules/to-vfile/node_modules/unist-util-stringify-position": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
"integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
"dependencies": {
"@types/unist": "^3.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/to-vfile/node_modules/vfile": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz",
"integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==",
"dependencies": {
"@types/unist": "^3.0.0",
"unist-util-stringify-position": "^4.0.0",
"vfile-message": "^4.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/to-vfile/node_modules/vfile-message": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz",
"integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==",
"dependencies": {
"@types/unist": "^3.0.0",
"unist-util-stringify-position": "^4.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/toidentifier": { "node_modules/toidentifier": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@ -19968,36 +20003,49 @@
} }
}, },
"mdast-util-find-and-replace": { "mdast-util-find-and-replace": {
"version": "2.2.2", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.2.tgz", "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz",
"integrity": "sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==", "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==",
"requires": { "requires": {
"@types/mdast": "^3.0.0", "@types/mdast": "^4.0.0",
"escape-string-regexp": "^5.0.0", "escape-string-regexp": "^5.0.0",
"unist-util-is": "^5.0.0", "unist-util-is": "^6.0.0",
"unist-util-visit-parents": "^5.0.0" "unist-util-visit-parents": "^6.0.0"
}, },
"dependencies": { "dependencies": {
"@types/mdast": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.0.tgz",
"integrity": "sha512-YLeG8CujC9adtj/kuDzq1N4tCDYKoZ5l/bnjq8d74+t/3q/tHquJOJKUQXJrLCflOHpKjXgcI/a929gpmLOEng==",
"requires": {
"@types/unist": "*"
}
},
"@types/unist": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.0.tgz",
"integrity": "sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w=="
},
"escape-string-regexp": { "escape-string-regexp": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==" "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="
}, },
"unist-util-is": { "unist-util-is": {
"version": "5.2.1", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
"integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
"requires": { "requires": {
"@types/unist": "^2.0.0" "@types/unist": "^3.0.0"
} }
}, },
"unist-util-visit-parents": { "unist-util-visit-parents": {
"version": "5.1.3", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
"integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
"requires": { "requires": {
"@types/unist": "^2.0.0", "@types/unist": "^3.0.0",
"unist-util-is": "^5.0.0" "unist-util-is": "^6.0.0"
} }
} }
} }
@ -20619,9 +20667,9 @@
} }
}, },
"postcss": { "postcss": {
"version": "8.4.30", "version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
"integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
"requires": { "requires": {
"nanoid": "^3.3.6", "nanoid": "^3.3.6",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
@ -21401,9 +21449,9 @@
} }
}, },
"react-tooltip": { "react-tooltip": {
"version": "5.21.4", "version": "5.21.5",
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.21.4.tgz", "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.21.5.tgz",
"integrity": "sha512-LZsllEbiu63zNwuCalq3gIFcBu2Xf0I0fMg7uuF7/5ROo5//uHe8Sum7v9L1Rtp6IozcoU9YAjkNUZdrxutsNg==", "integrity": "sha512-ey70qf6pBGi4U6xpyNlZAHobAhlo2dfxmImR2Bzd/DbLTsAYWz3TEaK+RMFuUZMq6hSPRbUHQSkP2rHBq4uFVg==",
"requires": { "requires": {
"@floating-ui/dom": "^1.0.0", "@floating-ui/dom": "^1.0.0",
"classnames": "^2.3.0" "classnames": "^2.3.0"
@ -21539,107 +21587,91 @@
"integrity": "sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ==" "integrity": "sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ=="
}, },
"remark-github": { "remark-github": {
"version": "11.2.4", "version": "12.0.0",
"resolved": "https://registry.npmjs.org/remark-github/-/remark-github-11.2.4.tgz", "resolved": "https://registry.npmjs.org/remark-github/-/remark-github-12.0.0.tgz",
"integrity": "sha512-GJjWFpwqdrHHhPWqMbb8+lqFLiHQ9pCzUmXmRrhMFXGpYov5n2ljsZzuWgXlfzArfQYkiKIZczA2I8IHYMHqCA==", "integrity": "sha512-ByefQKFN184LeiGRCabfl7zUJsdlMYWEhiLX1gpmQ11yFg6xSuOTW7LVCv0oc1x+YvUMJW23NU36sJX2RWGgvg==",
"requires": { "requires": {
"@types/mdast": "^3.0.0", "@types/mdast": "^4.0.0",
"mdast-util-find-and-replace": "^2.0.0", "mdast-util-find-and-replace": "^3.0.0",
"mdast-util-to-string": "^3.0.0", "mdast-util-to-string": "^4.0.0",
"unified": "^10.0.0", "to-vfile": "^8.0.0",
"unist-util-visit": "^4.0.0" "unist-util-visit": "^5.0.0",
"vfile": "^6.0.0"
}, },
"dependencies": { "dependencies": {
"bail": { "@types/mdast": {
"version": "2.0.2", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.0.tgz",
"integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==" "integrity": "sha512-YLeG8CujC9adtj/kuDzq1N4tCDYKoZ5l/bnjq8d74+t/3q/tHquJOJKUQXJrLCflOHpKjXgcI/a929gpmLOEng==",
},
"is-plain-obj": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
"integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="
},
"mdast-util-to-string": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz",
"integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==",
"requires": { "requires": {
"@types/mdast": "^3.0.0" "@types/unist": "*"
} }
}, },
"trough": { "@types/unist": {
"version": "2.1.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.0.tgz",
"integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==" "integrity": "sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w=="
}, },
"unified": { "mdast-util-to-string": {
"version": "10.1.2", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
"integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
"requires": { "requires": {
"@types/unist": "^2.0.0", "@types/mdast": "^4.0.0"
"bail": "^2.0.0",
"extend": "^3.0.0",
"is-buffer": "^2.0.0",
"is-plain-obj": "^4.0.0",
"trough": "^2.0.0",
"vfile": "^5.0.0"
} }
}, },
"unist-util-is": { "unist-util-is": {
"version": "5.2.1", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
"integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
"requires": { "requires": {
"@types/unist": "^2.0.0" "@types/unist": "^3.0.0"
} }
}, },
"unist-util-stringify-position": { "unist-util-stringify-position": {
"version": "3.0.3", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
"integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
"requires": { "requires": {
"@types/unist": "^2.0.0" "@types/unist": "^3.0.0"
} }
}, },
"unist-util-visit": { "unist-util-visit": {
"version": "4.1.2", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
"integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
"requires": { "requires": {
"@types/unist": "^2.0.0", "@types/unist": "^3.0.0",
"unist-util-is": "^5.0.0", "unist-util-is": "^6.0.0",
"unist-util-visit-parents": "^5.1.1" "unist-util-visit-parents": "^6.0.0"
} }
}, },
"unist-util-visit-parents": { "unist-util-visit-parents": {
"version": "5.1.3", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
"integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
"requires": { "requires": {
"@types/unist": "^2.0.0", "@types/unist": "^3.0.0",
"unist-util-is": "^5.0.0" "unist-util-is": "^6.0.0"
} }
}, },
"vfile": { "vfile": {
"version": "5.3.7", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz",
"integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==",
"requires": { "requires": {
"@types/unist": "^2.0.0", "@types/unist": "^3.0.0",
"is-buffer": "^2.0.0", "unist-util-stringify-position": "^4.0.0",
"unist-util-stringify-position": "^3.0.0", "vfile-message": "^4.0.0"
"vfile-message": "^3.0.0"
} }
}, },
"vfile-message": { "vfile-message": {
"version": "3.1.4", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz",
"integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==",
"requires": { "requires": {
"@types/unist": "^2.0.0", "@types/unist": "^3.0.0",
"unist-util-stringify-position": "^3.0.0" "unist-util-stringify-position": "^4.0.0"
} }
} }
} }
@ -22717,6 +22749,48 @@
"is-number": "^7.0.0" "is-number": "^7.0.0"
} }
}, },
"to-vfile": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/to-vfile/-/to-vfile-8.0.0.tgz",
"integrity": "sha512-IcmH1xB5576MJc9qcfEC/m/nQCFt3fzMHz45sSlgJyTWjRbKW1HAkJpuf3DgE57YzIlZcwcBZA5ENQbBo4aLkg==",
"requires": {
"vfile": "^6.0.0"
},
"dependencies": {
"@types/unist": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.0.tgz",
"integrity": "sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w=="
},
"unist-util-stringify-position": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
"integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
"requires": {
"@types/unist": "^3.0.0"
}
},
"vfile": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz",
"integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==",
"requires": {
"@types/unist": "^3.0.0",
"unist-util-stringify-position": "^4.0.0",
"vfile-message": "^4.0.0"
}
},
"vfile-message": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz",
"integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==",
"requires": {
"@types/unist": "^3.0.0",
"unist-util-stringify-position": "^4.0.0"
}
}
}
},
"toidentifier": { "toidentifier": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",

View File

@ -22,15 +22,15 @@
"@mdx-js/react": "^1.6.22", "@mdx-js/react": "^1.6.22",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"disqus-react": "^1.1.5", "disqus-react": "^1.1.5",
"postcss": "^8.4.30", "postcss": "^8.4.31",
"rapidoc": "^9.3.4", "rapidoc": "^9.3.4",
"react": "^17.0.2", "react": "^17.0.2",
"react-before-after-slider-component": "^1.1.8", "react-before-after-slider-component": "^1.1.8",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-feather": "^2.0.10", "react-feather": "^2.0.10",
"react-toggle": "^4.1.3", "react-toggle": "^4.1.3",
"react-tooltip": "^5.21.4", "react-tooltip": "^5.21.5",
"remark-github": "^11.2.4" "remark-github": "^12.0.0"
}, },
"browserslist": { "browserslist": {
"production": [ "production": [