Compare commits
	
		
			181 Commits
		
	
	
		
			version/20
			...
			esbuild-ts
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7c69add264 | |||
| 248fcd5d7f | |||
| 2c64e3f9ba | |||
| 99b559893b | |||
| 8014088c3a | |||
| 3ee353126f | |||
| db76c5d9e2 | |||
| 61bff69b7d | |||
| 69651323e3 | |||
| 75a0ac9588 | |||
| 941a697397 | |||
| 4a74db17a1 | |||
| 0cf6bff93c | |||
| 814e438422 | |||
| 2db77a37dd | |||
| e40c5ac617 | |||
| 7440900dac | |||
| ca96b27825 | |||
| ad4a765a80 | |||
| 4dcd481010 | |||
| d0dc14d84d | |||
| 7bf960352b | |||
| c07d01661b | |||
| 427597ec14 | |||
| 7cc77bd387 | |||
| 381a1a2c49 | |||
| 08f8222224 | |||
| 1211c34a18 | |||
| 22efb57369 | |||
| 3eeda53be6 | |||
| 82ace18703 | |||
| 8589079252 | |||
| ae2af6e58e | |||
| 86a7f98ff6 | |||
| 3af45371d3 | |||
| b01ffd934f | |||
| f11ba94603 | |||
| 7d2aa43364 | |||
| f1351a7577 | |||
| 0611eea0e7 | |||
| d0b46fcf9c | |||
| dcbdc37d31 | |||
| d07f396379 | |||
| 0972103b83 | |||
| b448e76db4 | |||
| f2937bd6dd | |||
| 53c2e3e77c | |||
| 7dd62c1f55 | |||
| 33e3510fba | |||
| 0e5fac2642 | |||
| c53b1fe78a | |||
| 838a7457b2 | |||
| a3c07bc9ff | |||
| 121f2c609d | |||
| 365affc28e | |||
| f367822779 | |||
| 848198125d | |||
| 497ac5e3d0 | |||
| 1773d4d681 | |||
| 4edbb51939 | |||
| c7e97ab48e | |||
| 31f7faae1c | |||
| f5dae2ae92 | |||
| 2c043dba0b | |||
| bda10e5db1 | |||
| be9ae7d4f7 | |||
| b4a6189bfa | |||
| bfdb827ff9 | |||
| 488a58e1c5 | |||
| 3f83e69453 | |||
| e92fa5df0b | |||
| f8c22170df | |||
| e3d08a8434 | |||
| 97d3e9afdc | |||
| 1eb08def73 | |||
| 6e3b379e4a | |||
| 264f59775c | |||
| d048f1ecbd | |||
| eb31f31584 | |||
| fe5c842e92 | |||
| b82d3100c9 | |||
| 49bb668036 | |||
| 52c70c7700 | |||
| b99fd36f86 | |||
| 8a5381eca3 | |||
| 2c77830179 | |||
| ffcd7def60 | |||
| ed121bc2a3 | |||
| d5ab9d9167 | |||
| a983321ad6 | |||
| 9c3420ede4 | |||
| 91b40350aa | |||
| 1912991682 | |||
| 71b9117f53 | |||
| b5f947f460 | |||
| 3a2f7e9549 | |||
| 1582ce0920 | |||
| 6d3eea5266 | |||
| e987208bd1 | |||
| 0efab8eef7 | |||
| 9402dac8ae | |||
| f57a290eee | |||
| 5dab0d2b7a | |||
| 2da6036248 | |||
| cdba94cea4 | |||
| c59eca664a | |||
| d5b205f9c0 | |||
| 8ad9ad833e | |||
| 599ce15f68 | |||
| 91310eff52 | |||
| b522d6732a | |||
| 17d96f204e | |||
| 65e4667bc3 | |||
| f67f9e5ed0 | |||
| 62dd6a4393 | |||
| a46eae8276 | |||
| c4acc9fc24 | |||
| e748a03082 | |||
| e473f28e21 | |||
| f70635c295 | |||
| 70d60c7ab2 | |||
| 61a26c02b7 | |||
| a06645d558 | |||
| 7730ecbd37 | |||
| 80e1be8db7 | |||
| c528c74e48 | |||
| 6d7bf36afe | |||
| 44fb59eb18 | |||
| 8f8d924935 | |||
| 602adaa5c5 | |||
| 5c9e97e11c | |||
| 2e7c620c9c | |||
| 30a2770781 | |||
| ef49fa0e79 | |||
| ac524ef425 | |||
| 6f3c1c4537 | |||
| 87886ca1b6 | |||
| 7ff96e30f9 | |||
| b26271557a | |||
| 15c99ff129 | |||
| 2a38e08e31 | |||
| 3696706466 | |||
| d0c9635033 | |||
| 7731014e1c | |||
| d478582a5c | |||
| 6255f380aa | |||
| 1f02e67c5c | |||
| d0bfb894b4 | |||
| c5dfdc6deb | |||
| d04a66ad9a | |||
| a5edaabec0 | |||
| daa367bc62 | |||
| 78345853c2 | |||
| f0fa8a3226 | |||
| 3335fdc6ad | |||
| 29c2c0f7dc | |||
| ada4254f52 | |||
| 39035de552 | |||
| e76d388ce4 | |||
| a52f887692 | |||
| d8b12a9a07 | |||
| ec01f16e99 | |||
| 9e3aaefc20 | |||
| 4454592442 | |||
| 593c953ecc | |||
| bcefe7123c | |||
| 812cf6c4f2 | |||
| 73b6ef6a73 | |||
| b58ebcddbf | |||
| 8b6ac3c806 | |||
| c6aa792076 | |||
| ee4792734e | |||
| 445f11ca6b | |||
| 8e4810fb20 | |||
| 96a122c5d1 | |||
| 3c6b8b10e5 | |||
| 15999caa5d | |||
| 57d8375de1 | |||
| 07ec787076 | |||
| bc96bef097 | |||
| 28869858b5 | 
| @ -1,5 +1,5 @@ | ||||
| [bumpversion] | ||||
| current_version = 2025.4.3 | ||||
| current_version = 2025.4.1 | ||||
| tag = True | ||||
| commit = True | ||||
| parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))? | ||||
|  | ||||
							
								
								
									
										2
									
								
								.github/actions/setup/action.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/actions/setup/action.yml
									
									
									
									
										vendored
									
									
								
							| @ -36,7 +36,7 @@ runs: | ||||
|       with: | ||||
|         go-version-file: "go.mod" | ||||
|     - name: Setup docker cache | ||||
|       uses: AndreKurait/docker-cache@0fe76702a40db986d9663c24954fc14c6a6031b7 | ||||
|       uses: ScribeMD/docker-cache@0.5.0 | ||||
|       with: | ||||
|         key: docker-images-${{ runner.os }}-${{ hashFiles('.github/actions/setup/docker-compose.yml', 'Makefile') }}-${{ inputs.postgresql_version }} | ||||
|     - name: Setup dependencies | ||||
|  | ||||
							
								
								
									
										12
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							| @ -118,3 +118,15 @@ updates: | ||||
|       prefix: "core:" | ||||
|     labels: | ||||
|       - dependencies | ||||
|   - package-ecosystem: docker-compose | ||||
|     directories: | ||||
|       # - /scripts # Maybe | ||||
|       - /tests/e2e | ||||
|     schedule: | ||||
|       interval: daily | ||||
|       time: "04:00" | ||||
|     open-pull-requests-limit: 10 | ||||
|     commit-message: | ||||
|       prefix: "core:" | ||||
|     labels: | ||||
|       - dependencies | ||||
|  | ||||
							
								
								
									
										3
									
								
								.github/workflows/ci-main.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/workflows/ci-main.yml
									
									
									
									
										vendored
									
									
								
							| @ -200,7 +200,7 @@ jobs: | ||||
|         uses: actions/cache@v4 | ||||
|         with: | ||||
|           path: web/dist | ||||
|           key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**') }} | ||||
|           key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b | ||||
|       - name: prepare web ui | ||||
|         if: steps.cache-web.outputs.cache-hit != 'true' | ||||
|         working-directory: web | ||||
| @ -208,6 +208,7 @@ jobs: | ||||
|           npm ci | ||||
|           make -C .. gen-client-ts | ||||
|           npm run build | ||||
|           npm run build:sfe | ||||
|       - name: run e2e | ||||
|         run: | | ||||
|           uv run coverage run manage.py test ${{ matrix.job.glob }} | ||||
|  | ||||
							
								
								
									
										2
									
								
								.github/workflows/ci-outpost.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci-outpost.yml
									
									
									
									
										vendored
									
									
								
							| @ -29,7 +29,7 @@ jobs: | ||||
|       - name: Generate API | ||||
|         run: make gen-client-go | ||||
|       - name: golangci-lint | ||||
|         uses: golangci/golangci-lint-action@v7 | ||||
|         uses: golangci/golangci-lint-action@v8 | ||||
|         with: | ||||
|           version: latest | ||||
|           args: --timeout 5000s --verbose | ||||
|  | ||||
							
								
								
									
										6
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							| @ -16,7 +16,7 @@ | ||||
|     ], | ||||
|     "typescript.preferences.importModuleSpecifier": "non-relative", | ||||
|     "typescript.preferences.importModuleSpecifierEnding": "index", | ||||
|     "typescript.tsdk": "./web/node_modules/typescript/lib", | ||||
|     "typescript.tsdk": "./node_modules/typescript/lib", | ||||
|     "typescript.enablePromptUseWorkspaceTsdk": true, | ||||
|     "yaml.schemas": { | ||||
|         "./blueprints/schema.json": "blueprints/**/*.yaml" | ||||
| @ -30,7 +30,5 @@ | ||||
|         } | ||||
|     ], | ||||
|     "go.testFlags": ["-count=1"], | ||||
|     "github-actions.workflows.pinned.workflows": [ | ||||
|         ".github/workflows/ci-main.yml" | ||||
|     ] | ||||
|     "github-actions.workflows.pinned.workflows": [".github/workflows/ci-main.yml"] | ||||
| } | ||||
|  | ||||
| @ -86,18 +86,17 @@ FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v7.1.0 AS geoip | ||||
| ENV GEOIPUPDATE_EDITION_IDS="GeoLite2-City GeoLite2-ASN" | ||||
| ENV GEOIPUPDATE_VERBOSE="1" | ||||
| ENV GEOIPUPDATE_ACCOUNT_ID_FILE="/run/secrets/GEOIPUPDATE_ACCOUNT_ID" | ||||
| ENV GEOIPUPDATE_LICENSE_KEY_FILE="/run/secrets/GEOIPUPDATE_LICENSE_KEY" | ||||
|  | ||||
| USER root | ||||
| RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \ | ||||
|     --mount=type=secret,id=GEOIPUPDATE_LICENSE_KEY \ | ||||
|     mkdir -p /usr/share/GeoIP && \ | ||||
|     /bin/sh -c "/usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0" | ||||
|     /bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0" | ||||
|  | ||||
| # Stage 5: Download uv | ||||
| FROM ghcr.io/astral-sh/uv:0.6.16 AS uv | ||||
| FROM ghcr.io/astral-sh/uv:0.7.4 AS uv | ||||
| # Stage 6: Base python image | ||||
| FROM ghcr.io/goauthentik/fips-python:3.12.10-slim-bookworm-fips AS python-base | ||||
| FROM ghcr.io/goauthentik/fips-python:3.13.3-slim-bookworm-fips AS python-base | ||||
|  | ||||
| ENV VENV_PATH="/ak-root/.venv" \ | ||||
|     PATH="/lifecycle:/ak-root/.venv/bin:$PATH" \ | ||||
|  | ||||
| @ -42,4 +42,4 @@ See [SECURITY.md](SECURITY.md) | ||||
|  | ||||
| ## 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 [contribution guide](https://docs.goauthentik.io/docs/developer-docs?utm_source=github). | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
|  | ||||
| from os import environ | ||||
|  | ||||
| __version__ = "2025.4.3" | ||||
| __version__ = "2025.4.1" | ||||
| ENV_GIT_HASH_KEY = "GIT_BUILD_HASH" | ||||
|  | ||||
|  | ||||
|  | ||||
| @ -54,7 +54,7 @@ def create_component(generator: SchemaGenerator, name, schema, type_=ResolvedCom | ||||
|     return component | ||||
|  | ||||
|  | ||||
| def postprocess_schema_responses(result, generator: SchemaGenerator, **kwargs):  # noqa: W0613 | ||||
| def postprocess_schema_responses(result, generator: SchemaGenerator, **kwargs): | ||||
|     """Workaround to set a default response for endpoints. | ||||
|     Workaround suggested at | ||||
|     <https://github.com/tfranzel/drf-spectacular/issues/119#issuecomment-656970357> | ||||
|  | ||||
| @ -164,9 +164,7 @@ class BlueprintEntry: | ||||
|         """Get the blueprint model, with yaml tags resolved if present""" | ||||
|         return str(self.tag_resolver(self.model, blueprint)) | ||||
|  | ||||
|     def get_permissions( | ||||
|         self, blueprint: "Blueprint" | ||||
|     ) -> Generator[BlueprintEntryPermission, None, None]: | ||||
|     def get_permissions(self, blueprint: "Blueprint") -> Generator[BlueprintEntryPermission]: | ||||
|         """Get permissions of this entry, with all yaml tags resolved""" | ||||
|         for perm in self.permissions: | ||||
|             yield BlueprintEntryPermission( | ||||
|  | ||||
| @ -5,10 +5,10 @@ from typing import Any | ||||
| from django.db.models import F, Q | ||||
| from django.db.models import Value as V | ||||
| from django.http.request import HttpRequest | ||||
| from sentry_sdk import get_current_span | ||||
|  | ||||
| from authentik import get_full_version | ||||
| from authentik.brands.models import Brand | ||||
| from authentik.lib.sentry import get_http_meta | ||||
| from authentik.tenants.models import Tenant | ||||
|  | ||||
| _q_default = Q(default=True) | ||||
| @ -32,13 +32,9 @@ def context_processor(request: HttpRequest) -> dict[str, Any]: | ||||
|     """Context Processor that injects brand object into every template""" | ||||
|     brand = getattr(request, "brand", DEFAULT_BRAND) | ||||
|     tenant = getattr(request, "tenant", Tenant()) | ||||
|     trace = "" | ||||
|     span = get_current_span() | ||||
|     if span: | ||||
|         trace = span.to_traceparent() | ||||
|     return { | ||||
|         "brand": brand, | ||||
|         "footer_links": tenant.footer_links, | ||||
|         "sentry_trace": trace, | ||||
|         "html_meta": {**get_http_meta()}, | ||||
|         "version": get_full_version(), | ||||
|     } | ||||
|  | ||||
| @ -79,7 +79,6 @@ def _migrate_session( | ||||
|         AuthenticatedSession.objects.using(db_alias).create( | ||||
|             session=session, | ||||
|             user=old_auth_session.user, | ||||
|             uuid=old_auth_session.uuid, | ||||
|         ) | ||||
|  | ||||
|  | ||||
|  | ||||
| @ -1,81 +1,10 @@ | ||||
| # Generated by Django 5.1.9 on 2025-05-14 11:15 | ||||
|  | ||||
| from django.apps.registry import Apps, apps as global_apps | ||||
| from django.apps.registry import Apps | ||||
| from django.db import migrations | ||||
| from django.contrib.contenttypes.management import create_contenttypes | ||||
| from django.contrib.auth.management import create_permissions | ||||
| from django.db.backends.base.schema import BaseDatabaseSchemaEditor | ||||
|  | ||||
|  | ||||
| def migrate_authenticated_session_permissions(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): | ||||
|     """Migrate permissions from OldAuthenticatedSession to AuthenticatedSession""" | ||||
|     db_alias = schema_editor.connection.alias | ||||
|  | ||||
|     # `apps` here is just an instance of `django.db.migrations.state.AppConfigStub`, we need the | ||||
|     # real config for creating permissions and content types | ||||
|     authentik_core_config = global_apps.get_app_config("authentik_core") | ||||
|     # These are only ran by django after all migrations, but we need them right now. | ||||
|     # `global_apps` is needed, | ||||
|     create_permissions(authentik_core_config, using=db_alias, verbosity=1) | ||||
|     create_contenttypes(authentik_core_config, using=db_alias, verbosity=1) | ||||
|  | ||||
|     # But from now on, this is just a regular migration, so use `apps` | ||||
|     Permission = apps.get_model("auth", "Permission") | ||||
|     ContentType = apps.get_model("contenttypes", "ContentType") | ||||
|  | ||||
|     try: | ||||
|         old_ct = ContentType.objects.using(db_alias).get( | ||||
|             app_label="authentik_core", model="oldauthenticatedsession" | ||||
|         ) | ||||
|         new_ct = ContentType.objects.using(db_alias).get( | ||||
|             app_label="authentik_core", model="authenticatedsession" | ||||
|         ) | ||||
|     except ContentType.DoesNotExist: | ||||
|         # This should exist at this point, but if not, let's cut our losses | ||||
|         return | ||||
|  | ||||
|     # Get all permissions for the old content type | ||||
|     old_perms = Permission.objects.using(db_alias).filter(content_type=old_ct) | ||||
|  | ||||
|     # Create equivalent permissions for the new content type | ||||
|     for old_perm in old_perms: | ||||
|         new_perm = ( | ||||
|             Permission.objects.using(db_alias) | ||||
|             .filter( | ||||
|                 content_type=new_ct, | ||||
|                 codename=old_perm.codename, | ||||
|             ) | ||||
|             .first() | ||||
|         ) | ||||
|         if not new_perm: | ||||
|             # This should exist at this point, but if not, let's cut our losses | ||||
|             continue | ||||
|  | ||||
|         # Global user permissions | ||||
|         User = apps.get_model("authentik_core", "User") | ||||
|         User.user_permissions.through.objects.using(db_alias).filter( | ||||
|             permission=old_perm | ||||
|         ).all().update(permission=new_perm) | ||||
|  | ||||
|         # Global role permissions | ||||
|         DjangoGroup = apps.get_model("auth", "Group") | ||||
|         DjangoGroup.permissions.through.objects.using(db_alias).filter( | ||||
|             permission=old_perm | ||||
|         ).all().update(permission=new_perm) | ||||
|  | ||||
|         # Object user permissions | ||||
|         UserObjectPermission = apps.get_model("guardian", "UserObjectPermission") | ||||
|         UserObjectPermission.objects.using(db_alias).filter(permission=old_perm).all().update( | ||||
|             permission=new_perm, content_type=new_ct | ||||
|         ) | ||||
|  | ||||
|         # Object role permissions | ||||
|         GroupObjectPermission = apps.get_model("guardian", "GroupObjectPermission") | ||||
|         GroupObjectPermission.objects.using(db_alias).filter(permission=old_perm).all().update( | ||||
|             permission=new_perm, content_type=new_ct | ||||
|         ) | ||||
|  | ||||
|  | ||||
| def remove_old_authenticated_session_content_type( | ||||
|     apps: Apps, schema_editor: BaseDatabaseSchemaEditor | ||||
| ): | ||||
| @ -92,12 +21,7 @@ class Migration(migrations.Migration): | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.RunPython( | ||||
|             code=migrate_authenticated_session_permissions, | ||||
|             reverse_code=migrations.RunPython.noop, | ||||
|         ), | ||||
|         migrations.RunPython( | ||||
|             code=remove_old_authenticated_session_content_type, | ||||
|             reverse_code=migrations.RunPython.noop, | ||||
|         ), | ||||
|     ] | ||||
|  | ||||
| @ -21,7 +21,9 @@ | ||||
|         <script src="{% versioned_script 'dist/standalone/loading/index-%v.js' %}" type="module"></script> | ||||
|         {% block head %} | ||||
|         {% endblock %} | ||||
|         <meta name="sentry-trace" content="{{ sentry_trace }}" /> | ||||
|         {% for key, value in html_meta.items %} | ||||
|         <meta name="{{key}}" content="{{ value }}" /> | ||||
|         {% endfor %} | ||||
|     </head> | ||||
|     <body> | ||||
|         {% block body %} | ||||
|  | ||||
| @ -57,7 +57,7 @@ class LogEventSerializer(PassiveSerializer): | ||||
|  | ||||
|  | ||||
| @contextmanager | ||||
| def capture_logs(log_default_output=True) -> Generator[list[LogEvent], None, None]: | ||||
| def capture_logs(log_default_output=True) -> Generator[list[LogEvent]]: | ||||
|     """Capture log entries created""" | ||||
|     logs = [] | ||||
|     cap = LogCapture() | ||||
|  | ||||
| @ -7,7 +7,7 @@ | ||||
| {{ block.super }} | ||||
| <link rel="prefetch" href="{{ flow_background_url }}" /> | ||||
| {% if flow.compatibility_mode and not inspector %} | ||||
| <script>ShadyDOM = { force: !navigator.webdriver };</script> | ||||
| <script>ShadyDOM = { force: true };</script> | ||||
| {% endif %} | ||||
| {% include "base/header_js.html" %} | ||||
| <script> | ||||
|  | ||||
| @ -17,7 +17,7 @@ from ldap3.core.exceptions import LDAPException | ||||
| from redis.exceptions import ConnectionError as RedisConnectionError | ||||
| from redis.exceptions import RedisError, ResponseError | ||||
| from rest_framework.exceptions import APIException | ||||
| from sentry_sdk import HttpTransport | ||||
| from sentry_sdk import HttpTransport, get_current_scope | ||||
| from sentry_sdk import init as sentry_sdk_init | ||||
| from sentry_sdk.api import set_tag | ||||
| from sentry_sdk.integrations.argv import ArgvIntegration | ||||
| @ -27,6 +27,7 @@ from sentry_sdk.integrations.redis import RedisIntegration | ||||
| from sentry_sdk.integrations.socket import SocketIntegration | ||||
| from sentry_sdk.integrations.stdlib import StdlibIntegration | ||||
| from sentry_sdk.integrations.threading import ThreadingIntegration | ||||
| from sentry_sdk.tracing import BAGGAGE_HEADER_NAME, SENTRY_TRACE_HEADER_NAME | ||||
| from structlog.stdlib import get_logger | ||||
| from websockets.exceptions import WebSocketException | ||||
|  | ||||
| @ -95,6 +96,8 @@ def traces_sampler(sampling_context: dict) -> float: | ||||
|         return 0 | ||||
|     if _type == "websocket": | ||||
|         return 0 | ||||
|     if CONFIG.get_bool("debug"): | ||||
|         return 1 | ||||
|     return float(CONFIG.get("error_reporting.sample_rate", 0.1)) | ||||
|  | ||||
|  | ||||
| @ -167,3 +170,14 @@ def before_send(event: dict, hint: dict) -> dict | None: | ||||
|     if settings.DEBUG: | ||||
|         return None | ||||
|     return event | ||||
|  | ||||
|  | ||||
| def get_http_meta(): | ||||
|     """Get sentry-related meta key-values""" | ||||
|     scope = get_current_scope() | ||||
|     meta = { | ||||
|         SENTRY_TRACE_HEADER_NAME: scope.get_traceparent() or "", | ||||
|     } | ||||
|     if bag := scope.get_baggage(): | ||||
|         meta[BAGGAGE_HEADER_NAME] = bag.serialize() | ||||
|     return meta | ||||
|  | ||||
| @ -59,7 +59,7 @@ class PropertyMappingManager: | ||||
|         request: HttpRequest | None, | ||||
|         return_mapping: bool = False, | ||||
|         **kwargs, | ||||
|     ) -> Generator[tuple[dict, PropertyMapping], None]: | ||||
|     ) -> Generator[tuple[dict, PropertyMapping]]: | ||||
|         """Iterate over all mappings that were pre-compiled and | ||||
|         execute all of them with the given context""" | ||||
|         if not self.__has_compiled: | ||||
|  | ||||
| @ -66,10 +66,7 @@ class RACClientConsumer(AsyncWebsocketConsumer): | ||||
|     def init_outpost_connection(self): | ||||
|         """Initialize guac connection settings""" | ||||
|         self.token = ( | ||||
|             ConnectionToken.filter_not_expired( | ||||
|                 token=self.scope["url_route"]["kwargs"]["token"], | ||||
|                 session__session__session_key=self.scope["session"].session_key, | ||||
|             ) | ||||
|             ConnectionToken.filter_not_expired(token=self.scope["url_route"]["kwargs"]["token"]) | ||||
|             .select_related("endpoint", "provider", "session", "session__user") | ||||
|             .first() | ||||
|         ) | ||||
|  | ||||
| @ -87,22 +87,3 @@ class TestRACViews(APITestCase): | ||||
|         ) | ||||
|         body = loads(flow_response.content) | ||||
|         self.assertEqual(body["component"], "ak-stage-access-denied") | ||||
|  | ||||
|     def test_different_session(self): | ||||
|         """Test request""" | ||||
|         self.client.force_login(self.user) | ||||
|         response = self.client.get( | ||||
|             reverse( | ||||
|                 "authentik_providers_rac:start", | ||||
|                 kwargs={"app": self.app.slug, "endpoint": str(self.endpoint.pk)}, | ||||
|             ) | ||||
|         ) | ||||
|         self.assertEqual(response.status_code, 302) | ||||
|         flow_response = self.client.get( | ||||
|             reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) | ||||
|         ) | ||||
|         body = loads(flow_response.content) | ||||
|         next_url = body["to"] | ||||
|         self.client.logout() | ||||
|         final_response = self.client.get(next_url) | ||||
|         self.assertEqual(final_response.url, reverse("authentik_core:if-user")) | ||||
|  | ||||
| @ -65,10 +65,7 @@ class RACInterface(InterfaceView): | ||||
|  | ||||
|     def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: | ||||
|         # Early sanity check to ensure token still exists | ||||
|         token = ConnectionToken.filter_not_expired( | ||||
|             token=self.kwargs["token"], | ||||
|             session__session__session_key=request.session.session_key, | ||||
|         ).first() | ||||
|         token = ConnectionToken.filter_not_expired(token=self.kwargs["token"]).first() | ||||
|         if not token: | ||||
|             return redirect("authentik_core:if-user") | ||||
|         self.token = token | ||||
|  | ||||
| @ -199,7 +199,7 @@ class SCIMGroupClient(SCIMClient[Group, SCIMProviderGroup, SCIMGroupSchema]): | ||||
|             chunk_size = len(ops) | ||||
|         if len(ops) < 1: | ||||
|             return | ||||
|         for chunk in batched(ops, chunk_size): | ||||
|         for chunk in batched(ops, chunk_size, strict=False): | ||||
|             req = PatchRequest(Operations=list(chunk)) | ||||
|             self._request( | ||||
|                 "PATCH", | ||||
|  | ||||
| @ -97,8 +97,7 @@ class GroupsView(SCIMObjectView): | ||||
|                     self.logger.warning("Invalid group member", exc=exc) | ||||
|                     continue | ||||
|                 query |= Q(uuid=member.value) | ||||
|             if query: | ||||
|                 group.users.set(User.objects.filter(query)) | ||||
|             group.users.set(User.objects.filter(query)) | ||||
|         if not connection: | ||||
|             connection, _ = SCIMSourceGroup.objects.get_or_create( | ||||
|                 source=self.source, | ||||
|  | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -2,7 +2,7 @@ | ||||
|     "$schema": "http://json-schema.org/draft-07/schema", | ||||
|     "$id": "https://goauthentik.io/blueprints/schema.json", | ||||
|     "type": "object", | ||||
|     "title": "authentik 2025.4.3 Blueprint schema", | ||||
|     "title": "authentik 2025.4.1 Blueprint schema", | ||||
|     "required": [ | ||||
|         "version", | ||||
|         "entries" | ||||
|  | ||||
| @ -31,7 +31,7 @@ services: | ||||
|     volumes: | ||||
|       - redis:/data | ||||
|   server: | ||||
|     image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.4.3} | ||||
|     image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.4.1} | ||||
|     restart: unless-stopped | ||||
|     command: server | ||||
|     environment: | ||||
| @ -55,7 +55,7 @@ services: | ||||
|       redis: | ||||
|         condition: service_healthy | ||||
|   worker: | ||||
|     image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.4.3} | ||||
|     image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.4.1} | ||||
|     restart: unless-stopped | ||||
|     command: worker | ||||
|     environment: | ||||
|  | ||||
							
								
								
									
										16
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								go.mod
									
									
									
									
									
								
							| @ -5,7 +5,7 @@ go 1.24.0 | ||||
| require ( | ||||
| 	beryju.io/ldap v0.1.0 | ||||
| 	github.com/coreos/go-oidc/v3 v3.14.1 | ||||
| 	github.com/getsentry/sentry-go v0.32.0 | ||||
| 	github.com/getsentry/sentry-go v0.33.0 | ||||
| 	github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1 | ||||
| 	github.com/go-ldap/ldap/v3 v3.4.11 | ||||
| 	github.com/go-openapi/runtime v0.28.0 | ||||
| @ -19,18 +19,18 @@ require ( | ||||
| 	github.com/jellydator/ttlcache/v3 v3.3.0 | ||||
| 	github.com/mitchellh/mapstructure v1.5.0 | ||||
| 	github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 | ||||
| 	github.com/pires/go-proxyproto v0.8.0 | ||||
| 	github.com/pires/go-proxyproto v0.8.1 | ||||
| 	github.com/prometheus/client_golang v1.22.0 | ||||
| 	github.com/redis/go-redis/v9 v9.7.3 | ||||
| 	github.com/sethvargo/go-envconfig v1.2.0 | ||||
| 	github.com/redis/go-redis/v9 v9.8.0 | ||||
| 	github.com/sethvargo/go-envconfig v1.3.0 | ||||
| 	github.com/sirupsen/logrus v1.9.3 | ||||
| 	github.com/spf13/cobra v1.9.1 | ||||
| 	github.com/stretchr/testify v1.10.0 | ||||
| 	github.com/wwt/guac v1.3.2 | ||||
| 	goauthentik.io/api/v3 v3.2025024.9 | ||||
| 	goauthentik.io/api/v3 v3.2025041.1 | ||||
| 	golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab | ||||
| 	golang.org/x/oauth2 v0.29.0 | ||||
| 	golang.org/x/sync v0.13.0 | ||||
| 	golang.org/x/oauth2 v0.30.0 | ||||
| 	golang.org/x/sync v0.14.0 | ||||
| 	gopkg.in/yaml.v2 v2.4.0 | ||||
| 	layeh.com/radius v0.0.0-20210819152912-ad72663a72ab | ||||
| ) | ||||
| @ -75,7 +75,7 @@ require ( | ||||
| 	go.opentelemetry.io/otel/trace v1.24.0 // indirect | ||||
| 	golang.org/x/crypto v0.36.0 // indirect | ||||
| 	golang.org/x/sys v0.31.0 // indirect | ||||
| 	golang.org/x/text v0.23.0 // indirect | ||||
| 	golang.org/x/text v0.24.0 // indirect | ||||
| 	google.golang.org/protobuf v1.36.5 // indirect | ||||
| 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||
| ) | ||||
|  | ||||
							
								
								
									
										36
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								go.sum
									
									
									
									
									
								
							| @ -69,8 +69,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m | ||||
| github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= | ||||
| github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= | ||||
| github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= | ||||
| github.com/getsentry/sentry-go v0.32.0 h1:YKs+//QmwE3DcYtfKRH8/KyOOF/I6Qnx7qYGNHCGmCY= | ||||
| github.com/getsentry/sentry-go v0.32.0/go.mod h1:CYNcMMz73YigoHljQRG+qPF+eMq8gG72XcGN/p71BAY= | ||||
| github.com/getsentry/sentry-go v0.33.0 h1:YWyDii0KGVov3xOaamOnF0mjOrqSjBqwv48UEzn7QFg= | ||||
| github.com/getsentry/sentry-go v0.33.0/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE= | ||||
| github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo= | ||||
| github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= | ||||
| github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= | ||||
| @ -230,8 +230,8 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+ | ||||
| github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= | ||||
| github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= | ||||
| github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= | ||||
| github.com/pires/go-proxyproto v0.8.0 h1:5unRmEAPbHXHuLjDg01CxJWf91cw3lKHc/0xzKpXEe0= | ||||
| github.com/pires/go-proxyproto v0.8.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY= | ||||
| github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0= | ||||
| github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU= | ||||
| github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||
| @ -245,14 +245,14 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ | ||||
| github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= | ||||
| github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= | ||||
| github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= | ||||
| github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= | ||||
| github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= | ||||
| github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= | ||||
| github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= | ||||
| github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= | ||||
| github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= | ||||
| github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= | ||||
| github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||||
| github.com/sethvargo/go-envconfig v1.2.0 h1:q3XkOZWkC+G1sMLCrw9oPGTjYexygLOXDmGUit1ti8Q= | ||||
| github.com/sethvargo/go-envconfig v1.2.0/go.mod h1:JLd0KFWQYzyENqnEPWWZ49i4vzZo/6nRidxI8YvGiHw= | ||||
| github.com/sethvargo/go-envconfig v1.3.0 h1:gJs+Fuv8+f05omTpwWIu6KmuseFAXKrIaOZSh8RMt0U= | ||||
| github.com/sethvargo/go-envconfig v1.3.0/go.mod h1:JLd0KFWQYzyENqnEPWWZ49i4vzZo/6nRidxI8YvGiHw= | ||||
| github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= | ||||
| github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= | ||||
| github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= | ||||
| @ -290,8 +290,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y | ||||
| go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= | ||||
| go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= | ||||
| go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= | ||||
| goauthentik.io/api/v3 v3.2025024.9 h1:i3tbkyotE32ZpJ729BsPWTuLQUdtZ54Li4aP1amZzsM= | ||||
| goauthentik.io/api/v3 v3.2025024.9/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw= | ||||
| goauthentik.io/api/v3 v3.2025041.1 h1:GAN6AoTmfnCGgx1SyM07jP4/LR/T3rkTEyShSBd3Co8= | ||||
| goauthentik.io/api/v3 v3.2025041.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw= | ||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| @ -358,16 +358,16 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ | ||||
| golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||
| golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||
| golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||
| golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= | ||||
| golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= | ||||
| golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= | ||||
| golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= | ||||
| golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||
| golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= | ||||
| golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= | ||||
| golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= | ||||
| golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= | ||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| @ -376,8 +376,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ | ||||
| golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= | ||||
| golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= | ||||
| golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= | ||||
| golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= | ||||
| golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| @ -412,8 +412,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | ||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= | ||||
| golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= | ||||
| golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= | ||||
| golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= | ||||
| golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
|  | ||||
| @ -29,4 +29,4 @@ func UserAgent() string { | ||||
| 	return fmt.Sprintf("authentik@%s", FullVersion()) | ||||
| } | ||||
|  | ||||
| const VERSION = "2025.4.3" | ||||
| const VERSION = "2025.4.1" | ||||
|  | ||||
| @ -83,8 +83,7 @@ if [[ "$1" == "server" ]]; then | ||||
|     run_authentik | ||||
| elif [[ "$1" == "worker" ]]; then | ||||
|     set_mode "worker" | ||||
|     shift | ||||
|     check_if_root "python -m manage worker $@" | ||||
|     check_if_root "python -m manage worker" | ||||
| elif [[ "$1" == "worker-status" ]]; then | ||||
|     wait_for_db | ||||
|     celery -A authentik.root.celery flower \ | ||||
|  | ||||
							
								
								
									
										8
									
								
								lifecycle/aws/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								lifecycle/aws/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -9,7 +9,7 @@ | ||||
|             "version": "0.0.0", | ||||
|             "license": "MIT", | ||||
|             "devDependencies": { | ||||
|                 "aws-cdk": "^2.1012.0", | ||||
|                 "aws-cdk": "^2.1015.0", | ||||
|                 "cross-env": "^7.0.3" | ||||
|             }, | ||||
|             "engines": { | ||||
| @ -17,9 +17,9 @@ | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/aws-cdk": { | ||||
|             "version": "2.1012.0", | ||||
|             "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1012.0.tgz", | ||||
|             "integrity": "sha512-C6jSWkqP0hkY2Cs300VJHjspmTXDTMfB813kwZvRbd/OsKBfTBJBbYU16VoLAp1LVEOnQMf8otSlaSgzVF0X9A==", | ||||
|             "version": "2.1015.0", | ||||
|             "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1015.0.tgz", | ||||
|             "integrity": "sha512-txd+yMVVybtLfiwT409+fahbP0SkiwhmQvQf6PVVYnWzDPSknxYlUNJHisHV4tJEcbHWn1QPsLmqqMT0bw8hBg==", | ||||
|             "dev": true, | ||||
|             "license": "Apache-2.0", | ||||
|             "bin": { | ||||
|  | ||||
| @ -10,7 +10,7 @@ | ||||
|         "node": ">=20" | ||||
|     }, | ||||
|     "devDependencies": { | ||||
|         "aws-cdk": "^2.1012.0", | ||||
|         "aws-cdk": "^2.1015.0", | ||||
|         "cross-env": "^7.0.3" | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -26,7 +26,7 @@ Parameters: | ||||
|     Description: authentik Docker image | ||||
|   AuthentikVersion: | ||||
|     Type: String | ||||
|     Default: 2025.4.3 | ||||
|     Default: 2025.4.1 | ||||
|     Description: authentik Docker image tag | ||||
|   AuthentikServerCPU: | ||||
|     Type: Number | ||||
|  | ||||
										
											Binary file not shown.
										
									
								
							| @ -12,8 +12,8 @@ | ||||
| # tmassimi, 2024 | ||||
| # Marc Schmitt, 2024 | ||||
| # albanobattistella <albanobattistella@gmail.com>, 2024 | ||||
| # Kowalski Dragon (kowalski7cc) <kowalski.7cc@gmail.com>, 2025 | ||||
| # Matteo Piccina <altermatte@gmail.com>, 2025 | ||||
| # Kowalski Dragon (kowalski7cc) <kowalski.7cc@gmail.com>, 2025 | ||||
| #  | ||||
| #, fuzzy | ||||
| msgid "" | ||||
| @ -22,7 +22,7 @@ msgstr "" | ||||
| "Report-Msgid-Bugs-To: \n" | ||||
| "POT-Creation-Date: 2025-04-23 09:00+0000\n" | ||||
| "PO-Revision-Date: 2022-09-26 16:47+0000\n" | ||||
| "Last-Translator: Matteo Piccina <altermatte@gmail.com>, 2025\n" | ||||
| "Last-Translator: Kowalski Dragon (kowalski7cc) <kowalski.7cc@gmail.com>, 2025\n" | ||||
| "Language-Team: Italian (https://app.transifex.com/authentik/teams/119923/it/)\n" | ||||
| "MIME-Version: 1.0\n" | ||||
| "Content-Type: text/plain; charset=UTF-8\n" | ||||
| @ -383,7 +383,7 @@ msgstr "Mappatura delle proprietà" | ||||
|  | ||||
| #: authentik/core/models.py | ||||
| msgid "session data" | ||||
| msgstr "" | ||||
| msgstr "dati sessione" | ||||
|  | ||||
| #: authentik/core/models.py | ||||
| msgid "Session" | ||||
| @ -509,7 +509,7 @@ msgstr "" | ||||
|  | ||||
| #: authentik/enterprise/policies/unique_password/models.py | ||||
| msgid "Number of passwords to check against." | ||||
| msgstr "" | ||||
| msgstr "Numero di password da verificare." | ||||
|  | ||||
| #: authentik/enterprise/policies/unique_password/models.py | ||||
| #: authentik/policies/password/models.py | ||||
| @ -519,18 +519,19 @@ msgstr "Password non impostata nel contesto" | ||||
| #: authentik/enterprise/policies/unique_password/models.py | ||||
| msgid "This password has been used previously. Please choose a different one." | ||||
| msgstr "" | ||||
| "Questa password è già stata utilizzata in precedenza. Scegline una diversa." | ||||
|  | ||||
| #: authentik/enterprise/policies/unique_password/models.py | ||||
| msgid "Password Uniqueness Policy" | ||||
| msgstr "" | ||||
| msgstr "Politica di unicità della password" | ||||
|  | ||||
| #: authentik/enterprise/policies/unique_password/models.py | ||||
| msgid "Password Uniqueness Policies" | ||||
| msgstr "" | ||||
| msgstr "Criteri di unicità delle password" | ||||
|  | ||||
| #: authentik/enterprise/policies/unique_password/models.py | ||||
| msgid "User Password History" | ||||
| msgstr "" | ||||
| msgstr "Cronologia password utente" | ||||
|  | ||||
| #: authentik/enterprise/policy.py | ||||
| msgid "Enterprise required to access this feature." | ||||
| @ -2203,7 +2204,7 @@ msgstr "Ruoli" | ||||
|  | ||||
| #: authentik/rbac/models.py | ||||
| msgid "Initial Permissions" | ||||
| msgstr "" | ||||
| msgstr "Permessi Iniziali" | ||||
|  | ||||
| #: authentik/rbac/models.py | ||||
| msgid "System permission" | ||||
| @ -2458,6 +2459,9 @@ msgid "" | ||||
| "attribute. This allows nested group resolution on systems like FreeIPA and " | ||||
| "Active Directory" | ||||
| msgstr "" | ||||
| "Cerca l'appartenenza al gruppo in base a un attributo utente anziché a un " | ||||
| "attributo di gruppo. Questo consente la risoluzione di gruppi nidificati su " | ||||
| "sistemi come FreeIPA e Active Directory." | ||||
|  | ||||
| #: authentik/sources/ldap/models.py | ||||
| msgid "LDAP Source" | ||||
| @ -2477,19 +2481,19 @@ msgstr "Mappature delle proprietà della sorgente LDAP" | ||||
|  | ||||
| #: authentik/sources/ldap/models.py | ||||
| msgid "User LDAP Source Connection" | ||||
| msgstr "" | ||||
| msgstr "Connessione Sorgente LDAP Utente" | ||||
|  | ||||
| #: authentik/sources/ldap/models.py | ||||
| msgid "User LDAP Source Connections" | ||||
| msgstr "" | ||||
| msgstr "Connessioni Sorgente LDAP Utente" | ||||
|  | ||||
| #: authentik/sources/ldap/models.py | ||||
| msgid "Group LDAP Source Connection" | ||||
| msgstr "" | ||||
| msgstr "Connessione Sorgente LDAP Gruppo" | ||||
|  | ||||
| #: authentik/sources/ldap/models.py | ||||
| msgid "Group LDAP Source Connections" | ||||
| msgstr "" | ||||
| msgstr "Connessioni Sorgente LDAP Gruppo" | ||||
|  | ||||
| #: authentik/sources/ldap/signals.py | ||||
| msgid "Password does not match Active Directory Complexity." | ||||
| @ -2501,11 +2505,11 @@ msgstr "Nessun token ricevuto." | ||||
|  | ||||
| #: authentik/sources/oauth/models.py | ||||
| msgid "HTTP Basic Authentication" | ||||
| msgstr "" | ||||
| msgstr "HTTP Basic Authentication" | ||||
|  | ||||
| #: authentik/sources/oauth/models.py | ||||
| msgid "Include the client ID and secret as request parameters" | ||||
| msgstr "" | ||||
| msgstr "Includi il client ID e il segreto come parametri di richiesta" | ||||
|  | ||||
| #: authentik/sources/oauth/models.py | ||||
| msgid "Request Token URL" | ||||
| @ -2552,6 +2556,8 @@ msgid "" | ||||
| "How to perform authentication during an authorization_code token request " | ||||
| "flow" | ||||
| msgstr "" | ||||
| "Come eseguire l'autenticazione durante un flusso di richiesta del token " | ||||
| "authorization_code" | ||||
|  | ||||
| #: authentik/sources/oauth/models.py | ||||
| msgid "OAuth Source" | ||||
| @ -3484,6 +3490,9 @@ msgid "" | ||||
| "Show the user the 'Remember me on this device' toggle, allowing repeat users" | ||||
| " to skip straight to entering their password." | ||||
| msgstr "" | ||||
| "Mostra all'utente il pulsante \"Ricordami su questo dispositivo\", " | ||||
| "consentendo agli utenti abituali di passare direttamente all'inserimento " | ||||
| "della password." | ||||
|  | ||||
| #: authentik/stages/identification/models.py | ||||
| msgid "Optional enrollment flow, which is linked at the bottom of the page." | ||||
| @ -3873,11 +3882,11 @@ msgstr "" | ||||
|  | ||||
| #: authentik/tenants/models.py | ||||
| msgid "Reputation cannot decrease lower than this value. Zero or negative." | ||||
| msgstr "" | ||||
| msgstr "La reputazione non può scendere sotto questo valore. Zero o negativo." | ||||
|  | ||||
| #: authentik/tenants/models.py | ||||
| msgid "Reputation cannot increase higher than this value. Zero or positive." | ||||
| msgstr "" | ||||
| msgstr "La reputazione non può superare questo valore. Zero o positivo." | ||||
|  | ||||
| #: authentik/tenants/models.py | ||||
| msgid "The option configures the footer links on the flow executor pages." | ||||
|  | ||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								locale/pt/LC_MESSAGES/django.mo
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								locale/pt/LC_MESSAGES/django.mo
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										3924
									
								
								locale/pt/LC_MESSAGES/django.po
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3924
									
								
								locale/pt/LC_MESSAGES/django.po
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										538
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										538
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -1,12 +1,546 @@ | ||||
| { | ||||
|     "name": "@goauthentik/authentik", | ||||
|     "version": "2025.2.1", | ||||
|     "version": "2025.4.0", | ||||
|     "lockfileVersion": 3, | ||||
|     "requires": true, | ||||
|     "packages": { | ||||
|         "": { | ||||
|             "name": "@goauthentik/authentik", | ||||
|             "version": "2025.2.1" | ||||
|             "version": "2025.4.0", | ||||
|             "devDependencies": { | ||||
|                 "@trivago/prettier-plugin-sort-imports": "^5.2.2", | ||||
|                 "prettier": "^3.3.3", | ||||
|                 "prettier-plugin-organize-imports": "^4.1.0", | ||||
|                 "prettier-plugin-packagejson": "^2.5.10", | ||||
|                 "typescript": "^5.6.2" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@babel/code-frame": { | ||||
|             "version": "7.26.2", | ||||
|             "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", | ||||
|             "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "@babel/helper-validator-identifier": "^7.25.9", | ||||
|                 "js-tokens": "^4.0.0", | ||||
|                 "picocolors": "^1.0.0" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">=6.9.0" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@babel/generator": { | ||||
|             "version": "7.27.0", | ||||
|             "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", | ||||
|             "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "@babel/parser": "^7.27.0", | ||||
|                 "@babel/types": "^7.27.0", | ||||
|                 "@jridgewell/gen-mapping": "^0.3.5", | ||||
|                 "@jridgewell/trace-mapping": "^0.3.25", | ||||
|                 "jsesc": "^3.0.2" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">=6.9.0" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@babel/helper-string-parser": { | ||||
|             "version": "7.25.9", | ||||
|             "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", | ||||
|             "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "engines": { | ||||
|                 "node": ">=6.9.0" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@babel/helper-validator-identifier": { | ||||
|             "version": "7.25.9", | ||||
|             "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", | ||||
|             "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "engines": { | ||||
|                 "node": ">=6.9.0" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@babel/parser": { | ||||
|             "version": "7.27.0", | ||||
|             "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", | ||||
|             "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "@babel/types": "^7.27.0" | ||||
|             }, | ||||
|             "bin": { | ||||
|                 "parser": "bin/babel-parser.js" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">=6.0.0" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@babel/template": { | ||||
|             "version": "7.27.0", | ||||
|             "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", | ||||
|             "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "@babel/code-frame": "^7.26.2", | ||||
|                 "@babel/parser": "^7.27.0", | ||||
|                 "@babel/types": "^7.27.0" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">=6.9.0" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@babel/traverse": { | ||||
|             "version": "7.27.0", | ||||
|             "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", | ||||
|             "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "@babel/code-frame": "^7.26.2", | ||||
|                 "@babel/generator": "^7.27.0", | ||||
|                 "@babel/parser": "^7.27.0", | ||||
|                 "@babel/template": "^7.27.0", | ||||
|                 "@babel/types": "^7.27.0", | ||||
|                 "debug": "^4.3.1", | ||||
|                 "globals": "^11.1.0" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">=6.9.0" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@babel/types": { | ||||
|             "version": "7.27.0", | ||||
|             "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", | ||||
|             "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "@babel/helper-string-parser": "^7.25.9", | ||||
|                 "@babel/helper-validator-identifier": "^7.25.9" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">=6.9.0" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@jridgewell/gen-mapping": { | ||||
|             "version": "0.3.8", | ||||
|             "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", | ||||
|             "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "@jridgewell/set-array": "^1.2.1", | ||||
|                 "@jridgewell/sourcemap-codec": "^1.4.10", | ||||
|                 "@jridgewell/trace-mapping": "^0.3.24" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">=6.0.0" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@jridgewell/resolve-uri": { | ||||
|             "version": "3.1.2", | ||||
|             "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", | ||||
|             "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "engines": { | ||||
|                 "node": ">=6.0.0" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@jridgewell/set-array": { | ||||
|             "version": "1.2.1", | ||||
|             "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", | ||||
|             "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "engines": { | ||||
|                 "node": ">=6.0.0" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@jridgewell/sourcemap-codec": { | ||||
|             "version": "1.5.0", | ||||
|             "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", | ||||
|             "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", | ||||
|             "dev": true, | ||||
|             "license": "MIT" | ||||
|         }, | ||||
|         "node_modules/@jridgewell/trace-mapping": { | ||||
|             "version": "0.3.25", | ||||
|             "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", | ||||
|             "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "@jridgewell/resolve-uri": "^3.1.0", | ||||
|                 "@jridgewell/sourcemap-codec": "^1.4.14" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@pkgr/core": { | ||||
|             "version": "0.1.2", | ||||
|             "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz", | ||||
|             "integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "engines": { | ||||
|                 "node": "^12.20.0 || ^14.18.0 || >=16.0.0" | ||||
|             }, | ||||
|             "funding": { | ||||
|                 "url": "https://opencollective.com/unts" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/@trivago/prettier-plugin-sort-imports": { | ||||
|             "version": "5.2.2", | ||||
|             "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-5.2.2.tgz", | ||||
|             "integrity": "sha512-fYDQA9e6yTNmA13TLVSA+WMQRc5Bn/c0EUBditUHNfMMxN7M82c38b1kEggVE3pLpZ0FwkwJkUEKMiOi52JXFA==", | ||||
|             "dev": true, | ||||
|             "license": "Apache-2.0", | ||||
|             "dependencies": { | ||||
|                 "@babel/generator": "^7.26.5", | ||||
|                 "@babel/parser": "^7.26.7", | ||||
|                 "@babel/traverse": "^7.26.7", | ||||
|                 "@babel/types": "^7.26.7", | ||||
|                 "javascript-natural-sort": "^0.7.1", | ||||
|                 "lodash": "^4.17.21" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">18.12" | ||||
|             }, | ||||
|             "peerDependencies": { | ||||
|                 "@vue/compiler-sfc": "3.x", | ||||
|                 "prettier": "2.x - 3.x", | ||||
|                 "prettier-plugin-svelte": "3.x", | ||||
|                 "svelte": "4.x || 5.x" | ||||
|             }, | ||||
|             "peerDependenciesMeta": { | ||||
|                 "@vue/compiler-sfc": { | ||||
|                     "optional": true | ||||
|                 }, | ||||
|                 "prettier-plugin-svelte": { | ||||
|                     "optional": true | ||||
|                 }, | ||||
|                 "svelte": { | ||||
|                     "optional": true | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/debug": { | ||||
|             "version": "4.4.0", | ||||
|             "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", | ||||
|             "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "ms": "^2.1.3" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">=6.0" | ||||
|             }, | ||||
|             "peerDependenciesMeta": { | ||||
|                 "supports-color": { | ||||
|                     "optional": true | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/detect-indent": { | ||||
|             "version": "7.0.1", | ||||
|             "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.1.tgz", | ||||
|             "integrity": "sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "engines": { | ||||
|                 "node": ">=12.20" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/detect-newline": { | ||||
|             "version": "4.0.1", | ||||
|             "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-4.0.1.tgz", | ||||
|             "integrity": "sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "engines": { | ||||
|                 "node": "^12.20.0 || ^14.13.1 || >=16.0.0" | ||||
|             }, | ||||
|             "funding": { | ||||
|                 "url": "https://github.com/sponsors/sindresorhus" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/fdir": { | ||||
|             "version": "6.4.4", | ||||
|             "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", | ||||
|             "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "peerDependencies": { | ||||
|                 "picomatch": "^3 || ^4" | ||||
|             }, | ||||
|             "peerDependenciesMeta": { | ||||
|                 "picomatch": { | ||||
|                     "optional": true | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/get-stdin": { | ||||
|             "version": "9.0.0", | ||||
|             "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", | ||||
|             "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "engines": { | ||||
|                 "node": ">=12" | ||||
|             }, | ||||
|             "funding": { | ||||
|                 "url": "https://github.com/sponsors/sindresorhus" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/git-hooks-list": { | ||||
|             "version": "3.2.0", | ||||
|             "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.2.0.tgz", | ||||
|             "integrity": "sha512-ZHG9a1gEhUMX1TvGrLdyWb9kDopCBbTnI8z4JgRMYxsijWipgjSEYoPWqBuIB0DnRnvqlQSEeVmzpeuPm7NdFQ==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "funding": { | ||||
|                 "url": "https://github.com/fisker/git-hooks-list?sponsor=1" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/globals": { | ||||
|             "version": "11.12.0", | ||||
|             "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", | ||||
|             "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "engines": { | ||||
|                 "node": ">=4" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/is-plain-obj": { | ||||
|             "version": "4.1.0", | ||||
|             "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", | ||||
|             "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "engines": { | ||||
|                 "node": ">=12" | ||||
|             }, | ||||
|             "funding": { | ||||
|                 "url": "https://github.com/sponsors/sindresorhus" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/javascript-natural-sort": { | ||||
|             "version": "0.7.1", | ||||
|             "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", | ||||
|             "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==", | ||||
|             "dev": true, | ||||
|             "license": "MIT" | ||||
|         }, | ||||
|         "node_modules/js-tokens": { | ||||
|             "version": "4.0.0", | ||||
|             "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", | ||||
|             "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", | ||||
|             "dev": true, | ||||
|             "license": "MIT" | ||||
|         }, | ||||
|         "node_modules/jsesc": { | ||||
|             "version": "3.1.0", | ||||
|             "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", | ||||
|             "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "bin": { | ||||
|                 "jsesc": "bin/jsesc" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">=6" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/lodash": { | ||||
|             "version": "4.17.21", | ||||
|             "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", | ||||
|             "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", | ||||
|             "dev": true, | ||||
|             "license": "MIT" | ||||
|         }, | ||||
|         "node_modules/ms": { | ||||
|             "version": "2.1.3", | ||||
|             "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", | ||||
|             "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", | ||||
|             "dev": true, | ||||
|             "license": "MIT" | ||||
|         }, | ||||
|         "node_modules/picocolors": { | ||||
|             "version": "1.1.1", | ||||
|             "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", | ||||
|             "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", | ||||
|             "dev": true, | ||||
|             "license": "ISC" | ||||
|         }, | ||||
|         "node_modules/picomatch": { | ||||
|             "version": "4.0.2", | ||||
|             "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", | ||||
|             "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "engines": { | ||||
|                 "node": ">=12" | ||||
|             }, | ||||
|             "funding": { | ||||
|                 "url": "https://github.com/sponsors/jonschlinkert" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/prettier": { | ||||
|             "version": "3.5.3", | ||||
|             "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", | ||||
|             "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "bin": { | ||||
|                 "prettier": "bin/prettier.cjs" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">=14" | ||||
|             }, | ||||
|             "funding": { | ||||
|                 "url": "https://github.com/prettier/prettier?sponsor=1" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/prettier-plugin-organize-imports": { | ||||
|             "version": "4.1.0", | ||||
|             "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", | ||||
|             "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "peerDependencies": { | ||||
|                 "prettier": ">=2.0", | ||||
|                 "typescript": ">=2.9", | ||||
|                 "vue-tsc": "^2.1.0" | ||||
|             }, | ||||
|             "peerDependenciesMeta": { | ||||
|                 "vue-tsc": { | ||||
|                     "optional": true | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/prettier-plugin-packagejson": { | ||||
|             "version": "2.5.10", | ||||
|             "resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.10.tgz", | ||||
|             "integrity": "sha512-LUxATI5YsImIVSaaLJlJ3aE6wTD+nvots18U3GuQMJpUyClChaZlQrqx3dBnbhF20OnKWZyx8EgyZypQtBDtgQ==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "sort-package-json": "2.15.1", | ||||
|                 "synckit": "0.9.2" | ||||
|             }, | ||||
|             "peerDependencies": { | ||||
|                 "prettier": ">= 1.16.0" | ||||
|             }, | ||||
|             "peerDependenciesMeta": { | ||||
|                 "prettier": { | ||||
|                     "optional": true | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/semver": { | ||||
|             "version": "7.7.1", | ||||
|             "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", | ||||
|             "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", | ||||
|             "dev": true, | ||||
|             "license": "ISC", | ||||
|             "bin": { | ||||
|                 "semver": "bin/semver.js" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">=10" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/sort-object-keys": { | ||||
|             "version": "1.1.3", | ||||
|             "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz", | ||||
|             "integrity": "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==", | ||||
|             "dev": true, | ||||
|             "license": "MIT" | ||||
|         }, | ||||
|         "node_modules/sort-package-json": { | ||||
|             "version": "2.15.1", | ||||
|             "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-2.15.1.tgz", | ||||
|             "integrity": "sha512-9x9+o8krTT2saA9liI4BljNjwAbvUnWf11Wq+i/iZt8nl2UGYnf3TH5uBydE7VALmP7AGwlfszuEeL8BDyb0YA==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "detect-indent": "^7.0.1", | ||||
|                 "detect-newline": "^4.0.0", | ||||
|                 "get-stdin": "^9.0.0", | ||||
|                 "git-hooks-list": "^3.0.0", | ||||
|                 "is-plain-obj": "^4.1.0", | ||||
|                 "semver": "^7.6.0", | ||||
|                 "sort-object-keys": "^1.1.3", | ||||
|                 "tinyglobby": "^0.2.9" | ||||
|             }, | ||||
|             "bin": { | ||||
|                 "sort-package-json": "cli.js" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/synckit": { | ||||
|             "version": "0.9.2", | ||||
|             "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", | ||||
|             "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "@pkgr/core": "^0.1.0", | ||||
|                 "tslib": "^2.6.2" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": "^14.18.0 || >=16.0.0" | ||||
|             }, | ||||
|             "funding": { | ||||
|                 "url": "https://opencollective.com/unts" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/tinyglobby": { | ||||
|             "version": "0.2.13", | ||||
|             "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", | ||||
|             "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", | ||||
|             "dev": true, | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "fdir": "^6.4.4", | ||||
|                 "picomatch": "^4.0.2" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">=12.0.0" | ||||
|             }, | ||||
|             "funding": { | ||||
|                 "url": "https://github.com/sponsors/SuperchupuDev" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/tslib": { | ||||
|             "version": "2.8.1", | ||||
|             "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", | ||||
|             "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", | ||||
|             "dev": true, | ||||
|             "license": "0BSD" | ||||
|         }, | ||||
|         "node_modules/typescript": { | ||||
|             "version": "5.8.3", | ||||
|             "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", | ||||
|             "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", | ||||
|             "dev": true, | ||||
|             "license": "Apache-2.0", | ||||
|             "bin": { | ||||
|                 "tsc": "bin/tsc", | ||||
|                 "tsserver": "bin/tsserver" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">=14.17" | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										14
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								package.json
									
									
									
									
									
								
							| @ -1,5 +1,15 @@ | ||||
| { | ||||
|     "name": "@goauthentik/authentik", | ||||
|     "version": "2025.4.3", | ||||
|     "private": true | ||||
|     "version": "2025.4.1", | ||||
|     "private": true, | ||||
|     "type": "module", | ||||
|     "devDependencies": { | ||||
|         "@trivago/prettier-plugin-sort-imports": "^5.2.2", | ||||
|         "prettier": "^3.3.3", | ||||
|         "prettier-plugin-organize-imports": "^4.1.0", | ||||
|         "prettier-plugin-packagejson": "^2.5.10", | ||||
|         "typescript": "^5.6.2" | ||||
|     }, | ||||
|     "workspaces": [], | ||||
|     "prettier": "./packages/prettier-config/index.js" | ||||
| } | ||||
|  | ||||
							
								
								
									
										4
									
								
								packages/docusaurus-config/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								packages/docusaurus-config/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -1,12 +1,12 @@ | ||||
| { | ||||
|     "name": "@goauthentik/docusaurus-config", | ||||
|     "version": "1.0.5", | ||||
|     "version": "1.0.6", | ||||
|     "lockfileVersion": 3, | ||||
|     "requires": true, | ||||
|     "packages": { | ||||
|         "": { | ||||
|             "name": "@goauthentik/docusaurus-config", | ||||
|             "version": "1.0.5", | ||||
|             "version": "1.0.6", | ||||
|             "license": "MIT", | ||||
|             "dependencies": { | ||||
|                 "deepmerge-ts": "^7.1.5", | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@goauthentik/docusaurus-config", | ||||
|     "version": "1.0.5", | ||||
|     "version": "1.0.6", | ||||
|     "description": "authentik's Docusaurus config", | ||||
|     "license": "MIT", | ||||
|     "scripts": { | ||||
|  | ||||
| @ -1,17 +0,0 @@ | ||||
| /** | ||||
|  * @file Constants for JavaScript and TypeScript files. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * The current Node.js environment, defaulting to "development" when not set. | ||||
|  * | ||||
|  * Note, this should only be used during the build process. | ||||
|  * | ||||
|  * If you need to check the environment at runtime, use `process.env.NODE_ENV` to | ||||
|  * ensure that module tree-shaking works correctly. | ||||
|  * | ||||
|  */ | ||||
| export const NodeEnvironment = /** @type {'development' | 'production'} */ ( | ||||
|     process.env.NODE_ENV || "development" | ||||
| ); | ||||
| @ -1,19 +0,0 @@ | ||||
| { | ||||
|     "name": "@goauthentik/monorepo", | ||||
|     "version": "1.0.0", | ||||
|     "description": "Utilities for the authentik monorepo.", | ||||
|     "private": true, | ||||
|     "license": "MIT", | ||||
|     "type": "module", | ||||
|     "exports": { | ||||
|         "./package.json": "./package.json", | ||||
|         ".": { | ||||
|             "import": "./index.js", | ||||
|             "types": "./out/index.d.ts" | ||||
|         } | ||||
|     }, | ||||
|     "types": "./out/index.d.ts", | ||||
|     "engines": { | ||||
|         "node": ">=20.11" | ||||
|     } | ||||
| } | ||||
| @ -1,30 +0,0 @@ | ||||
| import { createRequire } from "node:module"; | ||||
| import { dirname, join, resolve } from "node:path"; | ||||
| import { fileURLToPath } from "node:url"; | ||||
|  | ||||
| const __dirname = dirname(fileURLToPath(import.meta.url)); | ||||
|  | ||||
| /** | ||||
|  * @typedef {'~authentik'} MonoRepoRoot | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * The root of the authentik monorepo. | ||||
|  */ | ||||
| export const MonoRepoRoot = /** @type {MonoRepoRoot} */ (resolve(__dirname, "..", "..")); | ||||
|  | ||||
| const require = createRequire(import.meta.url); | ||||
|  | ||||
| /** | ||||
|  * Resolve a package name to its location in the monorepo to the single node_modules directory. | ||||
|  * @param {string} packageName | ||||
|  * @returns {string} The resolved path to the package. | ||||
|  * @throws {Error} If the package cannot be resolved. | ||||
|  */ | ||||
| export function resolvePackage(packageName) { | ||||
|     const packageJSONPath = require.resolve(join(packageName, "package.json"), { | ||||
|         paths: [MonoRepoRoot], | ||||
|     }); | ||||
|  | ||||
|     return dirname(packageJSONPath); | ||||
| } | ||||
							
								
								
									
										200
									
								
								pyproject.toml
									
									
									
									
									
								
							
							
						
						
									
										200
									
								
								pyproject.toml
									
									
									
									
									
								
							| @ -1,104 +1,116 @@ | ||||
| [project] | ||||
| name = "authentik" | ||||
| version = "2025.4.3" | ||||
| version = "2025.4.1" | ||||
| description = "" | ||||
| authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }] | ||||
| requires-python = "==3.12.*" | ||||
| requires-python = "==3.13.*" | ||||
| dependencies = [ | ||||
|     "argon2-cffi", | ||||
|     "celery", | ||||
|     "channels", | ||||
|     "channels-redis", | ||||
|     "cryptography", | ||||
|     "dacite", | ||||
|     "deepmerge", | ||||
|     "defusedxml", | ||||
|     "django", | ||||
|     "django-countries", | ||||
|     "django-cte", | ||||
|     "django-filter", | ||||
|     "django-guardian", | ||||
|     "django-model-utils", | ||||
|     "django-pglock", | ||||
|     "django-prometheus", | ||||
|     "django-redis", | ||||
|     "django-storages[s3]", | ||||
|     "django-tenants", | ||||
|     "djangorestframework", | ||||
|     "djangorestframework-guardian", | ||||
|     "docker", | ||||
|     "drf-orjson-renderer", | ||||
|     "drf-spectacular", | ||||
|     "dumb-init", | ||||
|     "duo-client", | ||||
|     "fido2", | ||||
|     "flower", | ||||
|     "geoip2", | ||||
|     "geopy", | ||||
|     "google-api-python-client", | ||||
|     "gssapi", | ||||
|     "gunicorn", | ||||
|     "jsonpatch", | ||||
|     "jwcrypto", | ||||
|     "kubernetes", | ||||
|     "ldap3", | ||||
|     "lxml", | ||||
|     "msgraph-sdk", | ||||
|     "opencontainers", | ||||
|     "packaging", | ||||
|     "paramiko", | ||||
|     "psycopg[c, pool]", | ||||
|     "pydantic", | ||||
|     "pydantic-scim", | ||||
|     "pyjwt", | ||||
|     "pyrad", | ||||
|     "python-kadmin-rs ==0.6.0", | ||||
|     "pyyaml", | ||||
|     "requests-oauthlib", | ||||
|     "scim2-filter-parser", | ||||
|     "sentry-sdk", | ||||
|     "service_identity", | ||||
|     "setproctitle", | ||||
|     "structlog", | ||||
|     "swagger-spec-validator", | ||||
|     "tenant-schemas-celery", | ||||
|     "twilio", | ||||
|     "ua-parser", | ||||
|     "unidecode", | ||||
|     "urllib3 <3", | ||||
|     "uvicorn[standard]", | ||||
|     "watchdog", | ||||
|     "webauthn", | ||||
|     "wsproto", | ||||
|     "xmlsec <= 1.3.14", | ||||
|     "zxcvbn", | ||||
|     "argon2-cffi==23.1.0", | ||||
|     "celery==5.5.2", | ||||
|     "channels==4.2.2", | ||||
|     "channels-redis==4.2.1", | ||||
|     "cryptography==44.0.3", | ||||
|     "dacite==1.9.2", | ||||
|     "deepmerge==2.0", | ||||
|     "defusedxml==0.7.1", | ||||
|     "django==5.1.9", | ||||
|     "django-countries==7.6.1", | ||||
|     "django-cte==1.3.3", | ||||
|     "django-filter==25.1", | ||||
|     "django-guardian<3.0.0", | ||||
|     "django-model-utils==5.0.0", | ||||
|     "django-pglock==1.7.2", | ||||
|     "django-prometheus==2.3.1", | ||||
|     "django-redis==5.4.0", | ||||
|     "django-storages[s3]==1.14.6", | ||||
|     "django-tenants==3.7.0", | ||||
|     "djangorestframework==3.16.0", | ||||
|     "djangorestframework-guardian==0.3.0", | ||||
|     "docker==7.1.0", | ||||
|     "drf-orjson-renderer==1.7.3", | ||||
|     "drf-spectacular==0.28.0", | ||||
|     "dumb-init==1.2.5.post1", | ||||
|     "duo-client==5.5.0", | ||||
|     "fido2==1.2.0", | ||||
|     "flower==2.0.1", | ||||
|     "geoip2==5.1.0", | ||||
|     "geopy==2.4.1", | ||||
|     "google-api-python-client==2.169.0", | ||||
|     "gssapi==1.9.0", | ||||
|     "gunicorn==23.0.0", | ||||
|     "jsonpatch==1.33", | ||||
|     "jwcrypto==1.5.6", | ||||
|     "kubernetes==32.0.1", | ||||
|     "ldap3==2.9.1", | ||||
|     "lxml==5.4.0", | ||||
|     "msgraph-sdk==1.30.0", | ||||
|     "opencontainers==0.0.14", | ||||
|     "packaging==25.0", | ||||
|     "paramiko==3.5.1", | ||||
|     "psycopg[c,pool]==3.2.9", | ||||
|     "pydantic==2.11.4", | ||||
|     "pydantic-scim==0.0.8", | ||||
|     "pyjwt==2.10.1", | ||||
|     "pyrad==2.4", | ||||
|     "python-kadmin-rs==0.6.0", | ||||
|     "pyyaml==6.0.2", | ||||
|     "requests-oauthlib==2.0.0", | ||||
|     "scim2-filter-parser==0.7.0", | ||||
|     "sentry-sdk==2.28.0", | ||||
|     "service-identity==24.2.0", | ||||
|     "setproctitle==1.3.6", | ||||
|     "structlog==25.3.0", | ||||
|     "swagger-spec-validator==3.0.4", | ||||
|     "tenant-schemas-celery==4.0.1", | ||||
|     "twilio==9.6.1", | ||||
|     "ua-parser==1.0.1", | ||||
|     "unidecode==1.4.0", | ||||
|     "urllib3<3", | ||||
|     "uvicorn[standard]==0.34.2", | ||||
|     "watchdog==6.0.0", | ||||
|     "webauthn==2.5.2", | ||||
|     "wsproto==1.2.0", | ||||
|     "xmlsec==1.3.15", | ||||
|     "zxcvbn==4.5.0", | ||||
| ] | ||||
|  | ||||
| [dependency-groups] | ||||
| dev = [ | ||||
|     "aws-cdk-lib", | ||||
|     "bandit", | ||||
|     "black", | ||||
|     "bump2version", | ||||
|     "channels[daphne]", | ||||
|     "codespell", | ||||
|     "colorama", | ||||
|     "constructs", | ||||
|     "coverage[toml]", | ||||
|     "debugpy", | ||||
|     "drf-jsonschema-serializer", | ||||
|     "freezegun", | ||||
|     "importlib-metadata", | ||||
|     "k5test", | ||||
|     "pdoc", | ||||
|     "pytest", | ||||
|     "pytest-django", | ||||
|     "pytest-github-actions-annotate-failures", | ||||
|     "pytest-randomly", | ||||
|     "pytest-timeout", | ||||
|     "requests-mock", | ||||
|     "ruff", | ||||
|     "selenium", | ||||
|     "aws-cdk-lib==2.188.0", | ||||
|     "bandit==1.8.3", | ||||
|     "black==25.1.0", | ||||
|     "bump2version==1.0.1", | ||||
|     "channels[daphne]==4.2.2", | ||||
|     "codespell==2.4.1", | ||||
|     "colorama==0.4.6", | ||||
|     "constructs==10.4.2", | ||||
|     "coverage[toml]==7.8.0", | ||||
|     "debugpy==1.8.14", | ||||
|     "drf-jsonschema-serializer==3.0.0", | ||||
|     "freezegun==1.5.1", | ||||
|     "importlib-metadata==8.6.1", | ||||
|     "k5test==0.10.4", | ||||
|     "pdoc==15.0.3", | ||||
|     "pytest==8.3.5", | ||||
|     "pytest-django==4.11.1", | ||||
|     "pytest-github-actions-annotate-failures==0.3.0", | ||||
|     "pytest-randomly==3.16.0", | ||||
|     "pytest-timeout==2.4.0", | ||||
|     "requests-mock==1.12.1", | ||||
|     "ruff==0.11.9", | ||||
|     "selenium==4.32.0", | ||||
| ] | ||||
|  | ||||
| [tool.uv] | ||||
| no-binary-package = [ | ||||
|     # This differs from the no-binary packages in the Dockerfile. This is due to the fact | ||||
|     # that these packages are built from source for different reasons than cryptography and kadmin. | ||||
|     # These packages are built from source to link against the libxml2 on the system which is | ||||
|     # required for functionality and to stay up-to-date on both libraries. | ||||
|     # The other packages specified in the dockerfile are compiled from source to link against the | ||||
|     # correct FIPS OpenSSL libraries | ||||
|     "lxml", | ||||
|     "xmlsec", | ||||
| ] | ||||
|  | ||||
| [tool.uv.sources] | ||||
| @ -143,12 +155,12 @@ ignore-words = ".github/codespell-words.txt" | ||||
|  | ||||
| [tool.black] | ||||
| line-length = 100 | ||||
| target-version = ['py312'] | ||||
| target-version = ['py313'] | ||||
| exclude = 'node_modules' | ||||
|  | ||||
| [tool.ruff] | ||||
| line-length = 100 | ||||
| target-version = "py312" | ||||
| target-version = "py313" | ||||
| exclude = ["**/migrations/**", "**/node_modules/**"] | ||||
|  | ||||
| [tool.ruff.lint] | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| openapi: 3.0.3 | ||||
| info: | ||||
|   title: authentik | ||||
|   version: 2025.4.3 | ||||
|   version: 2025.4.1 | ||||
|   description: Making authentication simple. | ||||
|   contact: | ||||
|     email: hello@goauthentik.io | ||||
|  | ||||
| @ -1,12 +1,12 @@ | ||||
| services: | ||||
|   chrome: | ||||
|     image: docker.io/selenium/standalone-chrome:122.0 | ||||
|     image: docker.io/selenium/standalone-chrome:136.0 | ||||
|     volumes: | ||||
|       - /dev/shm:/dev/shm | ||||
|     network_mode: host | ||||
|     restart: always | ||||
|   mailpit: | ||||
|     image: docker.io/axllent/mailpit:v1.6.5 | ||||
|     image: docker.io/axllent/mailpit:v1.24.2 | ||||
|     ports: | ||||
|       - 1025:1025 | ||||
|       - 8025:8025 | ||||
|  | ||||
| @ -1,12 +1,19 @@ | ||||
| """test default login flow""" | ||||
|  | ||||
| from authentik.blueprints.tests import apply_blueprint | ||||
| from authentik.flows.models import Flow | ||||
| from tests.e2e.utils import SeleniumTestCase, retry | ||||
|  | ||||
|  | ||||
| class TestFlowsLogin(SeleniumTestCase): | ||||
|     """test default login flow""" | ||||
|  | ||||
|     def tearDown(self): | ||||
|         # Reset authentication flow's compatibility mode; we need to do this as its | ||||
|         # not specified in the blueprint | ||||
|         Flow.objects.filter(slug="default-authentication-flow").update(compatibility_mode=False) | ||||
|         return super().tearDown() | ||||
|  | ||||
|     @retry() | ||||
|     @apply_blueprint( | ||||
|         "default/flow-default-authentication-flow.yaml", | ||||
| @ -23,3 +30,21 @@ class TestFlowsLogin(SeleniumTestCase): | ||||
|         self.login() | ||||
|         self.wait_for_url(self.if_user_url("/library")) | ||||
|         self.assert_user(self.user) | ||||
|  | ||||
|     @retry() | ||||
|     @apply_blueprint( | ||||
|         "default/flow-default-authentication-flow.yaml", | ||||
|         "default/flow-default-invalidation-flow.yaml", | ||||
|     ) | ||||
|     def test_login_compatibility_mode(self): | ||||
|         """test default login flow with compatibility mode enabled""" | ||||
|         Flow.objects.filter(slug="default-authentication-flow").update(compatibility_mode=True) | ||||
|         self.driver.get( | ||||
|             self.url( | ||||
|                 "authentik_core:if-flow", | ||||
|                 flow_slug="default-authentication-flow", | ||||
|             ) | ||||
|         ) | ||||
|         self.login(shadow_dom=False) | ||||
|         self.wait_for_url(self.if_user_url("/library")) | ||||
|         self.assert_user(self.user) | ||||
|  | ||||
							
								
								
									
										51
									
								
								tests/e2e/test_flows_login_sfe.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								tests/e2e/test_flows_login_sfe.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | ||||
| """test default login (using SFE interface) flow""" | ||||
|  | ||||
| from time import sleep | ||||
|  | ||||
| from selenium.webdriver.common.by import By | ||||
| from selenium.webdriver.common.keys import Keys | ||||
|  | ||||
| from authentik.blueprints.tests import apply_blueprint | ||||
| from tests.e2e.utils import SeleniumTestCase, retry | ||||
|  | ||||
|  | ||||
| class TestFlowsLoginSFE(SeleniumTestCase): | ||||
|     """test default login flow""" | ||||
|  | ||||
|     def login(self): | ||||
|         """Do entire login flow adjusted for SFE""" | ||||
|         flow_executor = self.driver.find_element(By.ID, "flow-sfe-container") | ||||
|         identification_stage = flow_executor.find_element(By.ID, "ident-form") | ||||
|  | ||||
|         identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").click() | ||||
|         identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").send_keys( | ||||
|             self.user.username | ||||
|         ) | ||||
|         identification_stage.find_element(By.CSS_SELECTOR, "input[name=uid_field]").send_keys( | ||||
|             Keys.ENTER | ||||
|         ) | ||||
|  | ||||
|         password_stage = flow_executor.find_element(By.ID, "password-form") | ||||
|         password_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys( | ||||
|             self.user.username | ||||
|         ) | ||||
|         password_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys(Keys.ENTER) | ||||
|         sleep(1) | ||||
|  | ||||
|     @retry() | ||||
|     @apply_blueprint( | ||||
|         "default/flow-default-authentication-flow.yaml", | ||||
|         "default/flow-default-invalidation-flow.yaml", | ||||
|     ) | ||||
|     def test_login(self): | ||||
|         """test default login flow""" | ||||
|         self.driver.get( | ||||
|             self.url( | ||||
|                 "authentik_core:if-flow", | ||||
|                 flow_slug="default-authentication-flow", | ||||
|                 query={"sfe": True}, | ||||
|             ) | ||||
|         ) | ||||
|         self.login() | ||||
|         self.wait_for_url(self.if_user_url("/library")) | ||||
|         self.assert_user(self.user) | ||||
| @ -26,8 +26,10 @@ from selenium import webdriver | ||||
| from selenium.common.exceptions import NoSuchElementException, TimeoutException, WebDriverException | ||||
| from selenium.webdriver.common.by import By | ||||
| from selenium.webdriver.common.keys import Keys | ||||
| from selenium.webdriver.remote.command import Command | ||||
| from selenium.webdriver.remote.webdriver import WebDriver | ||||
| from selenium.webdriver.remote.webelement import WebElement | ||||
| from selenium.webdriver.support import expected_conditions as ec | ||||
| from selenium.webdriver.support.wait import WebDriverWait | ||||
| from structlog.stdlib import get_logger | ||||
|  | ||||
| @ -36,8 +38,8 @@ from authentik.core.models import User | ||||
| from authentik.core.tests.utils import create_test_admin_user | ||||
| from authentik.lib.generators import generate_id | ||||
|  | ||||
| RETRIES = int(environ.get("RETRIES", "3")) | ||||
| IS_CI = "CI" in environ | ||||
| RETRIES = int(environ.get("RETRIES", "3")) if IS_CI else 1 | ||||
|  | ||||
|  | ||||
| def get_docker_tag() -> str: | ||||
| @ -197,7 +199,12 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase): | ||||
|         super().tearDown() | ||||
|         if IS_CI: | ||||
|             print("::group::Browser logs") | ||||
|         for line in self.driver.get_log("browser"): | ||||
|         # Very verbose way to get browser logs | ||||
|         # https://github.com/SeleniumHQ/selenium/pull/15641 | ||||
|         # for some reason this removes the `get_log` API from Remote Webdriver | ||||
|         # and only keeps it on the local Chrome web driver, even when using | ||||
|         # a remote chrome driver...? (nvm the fact this was released as a minor version) | ||||
|         for line in self.driver.execute(Command.GET_LOG, {"type": "browser"})["value"]: | ||||
|             print(line["message"]) | ||||
|         if IS_CI: | ||||
|             print("::endgroup::") | ||||
| @ -234,10 +241,30 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase): | ||||
|         element = self.driver.execute_script("return arguments[0].shadowRoot", shadow_root) | ||||
|         return element | ||||
|  | ||||
|     def login(self): | ||||
|         """Do entire login flow and check user afterwards""" | ||||
|         flow_executor = self.get_shadow_root("ak-flow-executor") | ||||
|         identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor) | ||||
|     def shady_dom(self) -> WebElement: | ||||
|         class wrapper: | ||||
|             def __init__(self, container: WebDriver): | ||||
|                 self.container = container | ||||
|  | ||||
|             def find_element(self, by: str, selector: str) -> WebElement: | ||||
|                 return self.container.execute_script( | ||||
|                     "return document.__shady_native_querySelector(arguments[0])", selector | ||||
|                 ) | ||||
|  | ||||
|         return wrapper(self.driver) | ||||
|  | ||||
|     def login(self, shadow_dom=True): | ||||
|         """Do entire login flow""" | ||||
|  | ||||
|         if shadow_dom: | ||||
|             flow_executor = self.get_shadow_root("ak-flow-executor") | ||||
|             identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor) | ||||
|         else: | ||||
|             flow_executor = self.shady_dom() | ||||
|             identification_stage = self.shady_dom() | ||||
|  | ||||
|         wait = WebDriverWait(identification_stage, self.wait_timeout) | ||||
|         wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "input[name=uidField]"))) | ||||
|  | ||||
|         identification_stage.find_element(By.CSS_SELECTOR, "input[name=uidField]").click() | ||||
|         identification_stage.find_element(By.CSS_SELECTOR, "input[name=uidField]").send_keys( | ||||
| @ -247,8 +274,16 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase): | ||||
|             Keys.ENTER | ||||
|         ) | ||||
|  | ||||
|         flow_executor = self.get_shadow_root("ak-flow-executor") | ||||
|         password_stage = self.get_shadow_root("ak-stage-password", flow_executor) | ||||
|         if shadow_dom: | ||||
|             flow_executor = self.get_shadow_root("ak-flow-executor") | ||||
|             password_stage = self.get_shadow_root("ak-stage-password", flow_executor) | ||||
|         else: | ||||
|             flow_executor = self.shady_dom() | ||||
|             password_stage = self.shady_dom() | ||||
|  | ||||
|         wait = WebDriverWait(password_stage, self.wait_timeout) | ||||
|         wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "input[name=password]"))) | ||||
|  | ||||
|         password_stage.find_element(By.CSS_SELECTOR, "input[name=password]").send_keys( | ||||
|             self.user.username | ||||
|         ) | ||||
|  | ||||
							
								
								
									
										28
									
								
								tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| // TypeScript Project Configuration | ||||
| { | ||||
|     "extends": "./packages/tsconfig/tsconfig.json", | ||||
|     "compilerOptions": { | ||||
|         "baseUrl": "." | ||||
|     }, | ||||
|     "watchOptions": { | ||||
|         "excludeDirectories": [ | ||||
|             "**/.git", // Git | ||||
|             "**/.yarn", // Yarn | ||||
|             "**/.vscode", // VS Code | ||||
|             "**/.vscode-test-web", // VS Code Web Test | ||||
|             "**/dist", // Distributed build files | ||||
|             "**/out", // Output build files | ||||
|             "**/.drafts", // Drafts | ||||
|             "**/.github", // GitHub | ||||
|             "**/node_modules" // Node modules | ||||
|         ] | ||||
|     }, | ||||
|  | ||||
|     // The root project has no sources of its own. By setting `files` to an empty | ||||
|     // list, TS won't automatically include all sources below root (the default). | ||||
|     "files": [], | ||||
|     "references": [ | ||||
|         // Note that references are in the order we want them to be built. | ||||
|         // TODO: Left blank until TypeScript workspaces are complete. | ||||
|     ] | ||||
| } | ||||
| @ -2,15 +2,11 @@ | ||||
| node_modules | ||||
| # don't lint build output (make sure it's set to your correct build folder name) | ||||
| dist | ||||
| out | ||||
| # don't lint nyc coverage output | ||||
| coverage | ||||
| # Import order matters | ||||
| poly.ts | ||||
| src/locale-codes.ts | ||||
| src/locales/ | ||||
| storybook-static/ | ||||
| # Prettier breaks the tsconfig file | ||||
| tsconfig.json | ||||
| .storybook/css-import-maps* | ||||
| package.json | ||||
| packages/**/package.json | ||||
|  | ||||
| @ -1,11 +0,0 @@ | ||||
| import { create } from "@storybook/theming/create"; | ||||
|  | ||||
| const isDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches; | ||||
|  | ||||
| export default create({ | ||||
|     base: isDarkMode ? "dark" : "light", | ||||
|     brandTitle: "authentik Storybook", | ||||
|     brandUrl: "https://goauthentik.io", | ||||
|     brandImage: "https://goauthentik.io/img/icon_left_brand_colour.svg", | ||||
|     brandTarget: "_self", | ||||
| }); | ||||
							
								
								
									
										63
									
								
								web/.storybook/main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								web/.storybook/main.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,63 @@ | ||||
| /** | ||||
|  * @file Storybook configuration. | ||||
|  * @import { StorybookConfig } from "@storybook/web-components-vite"; | ||||
|  * @import { InlineConfig, Plugin } from "vite"; | ||||
|  */ | ||||
| import { createBundleDefinitions } from "@goauthentik/web/scripts/esbuild/environment"; | ||||
| import postcssLit from "rollup-plugin-postcss-lit"; | ||||
| import tsconfigPaths from "vite-tsconfig-paths"; | ||||
|  | ||||
| const CSSImportPattern = /import [\w$]+ from .+\.(css)/g; | ||||
| const JavaScriptFilePattern = /\.m?(js|ts|tsx)$/; | ||||
|  | ||||
| /** | ||||
|  * @satisfies {Plugin<never>} | ||||
|  */ | ||||
| const inlineCSSPlugin = { | ||||
|     name: "inline-css-plugin", | ||||
|     transform: (source, id) => { | ||||
|         if (!JavaScriptFilePattern.test(id)) return; | ||||
|  | ||||
|         const code = source.replace(CSSImportPattern, (match) => { | ||||
|             return `${match}?inline`; | ||||
|         }); | ||||
|  | ||||
|         return { | ||||
|             code, | ||||
|         }; | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @satisfies {StorybookConfig} | ||||
|  */ | ||||
| const config = { | ||||
|     stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], | ||||
|     addons: [ | ||||
|         "@storybook/addon-controls", | ||||
|         "@storybook/addon-links", | ||||
|         "@storybook/addon-essentials", | ||||
|         "storybook-addon-mock", | ||||
|     ], | ||||
|     framework: { | ||||
|         name: "@storybook/web-components-vite", | ||||
|         options: {}, | ||||
|     }, | ||||
|     docs: { | ||||
|         autodocs: "tag", | ||||
|     }, | ||||
|     viteFinal({ plugins = [], ...config }) { | ||||
|         /** | ||||
|          * @satisfies {InlineConfig} | ||||
|          */ | ||||
|         const mergedConfig = { | ||||
|             ...config, | ||||
|             define: createBundleDefinitions(), | ||||
|             plugins: [inlineCSSPlugin, ...plugins, postcssLit(), tsconfigPaths()], | ||||
|         }; | ||||
|  | ||||
|         return mergedConfig; | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| export default config; | ||||
| @ -1,81 +0,0 @@ | ||||
| import replace from "@rollup/plugin-replace"; | ||||
| import type { StorybookConfig } from "@storybook/web-components-vite"; | ||||
| import { cwd } from "process"; | ||||
| import modify from "rollup-plugin-modify"; | ||||
| import postcssLit from "rollup-plugin-postcss-lit"; | ||||
| import tsconfigPaths from "vite-tsconfig-paths"; | ||||
|  | ||||
| export const isProdBuild = process.env.NODE_ENV === "production"; | ||||
| export const apiBasePath = process.env.AK_API_BASE_PATH || ""; | ||||
|  | ||||
| const importInlinePatterns = [ | ||||
|     'import AKGlobal from "(\\.\\./)*common/styles/authentik\\.css', | ||||
|     'import AKGlobal from "@goauthentik/common/styles/authentik\\.css', | ||||
|     'import PF.+ from "@patternfly/patternfly/\\S+\\.css', | ||||
|     'import ThemeDark from "@goauthentik/common/styles/theme-dark\\.css', | ||||
|     'import OneDark from "@goauthentik/common/styles/one-dark\\.css', | ||||
|     'import styles from "\\./LibraryPageImpl\\.css', | ||||
| ]; | ||||
|  | ||||
| const importInlineRegexp = new RegExp(importInlinePatterns.map((a) => `(${a})`).join("|")); | ||||
|  | ||||
| const config: StorybookConfig = { | ||||
|     stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], | ||||
|     addons: [ | ||||
|         "@storybook/addon-controls", | ||||
|         "@storybook/addon-links", | ||||
|         "@storybook/addon-essentials", | ||||
|         "storybook-addon-mock", | ||||
|     ], | ||||
|     staticDirs: [ | ||||
|         { | ||||
|             from: "../node_modules/@patternfly/patternfly/patternfly-base.css", | ||||
|             to: "@patternfly/patternfly/patternfly-base.css", | ||||
|         }, | ||||
|         { | ||||
|             from: "../src/common/styles/authentik.css", | ||||
|             to: "@goauthentik/common/styles/authentik.css", | ||||
|         }, | ||||
|         { | ||||
|             from: "../src/common/styles/theme-dark.css", | ||||
|             to: "@goauthentik/common/styles/theme-dark.css", | ||||
|         }, | ||||
|         { | ||||
|             from: "../src/common/styles/one-dark.css", | ||||
|             to: "@goauthentik/common/styles/one-dark.css", | ||||
|         }, | ||||
|     ], | ||||
|     framework: { | ||||
|         name: "@storybook/web-components-vite", | ||||
|         options: {}, | ||||
|     }, | ||||
|     docs: { | ||||
|         autodocs: "tag", | ||||
|     }, | ||||
|     async viteFinal(config) { | ||||
|         return { | ||||
|             ...config, | ||||
|             plugins: [ | ||||
|                 modify({ | ||||
|                     find: importInlineRegexp, | ||||
|                     replace: (match: RegExpMatchArray) => { | ||||
|                         return `${match}?inline`; | ||||
|                     }, | ||||
|                 }), | ||||
|                 replace({ | ||||
|                     "process.env.NODE_ENV": JSON.stringify( | ||||
|                         isProdBuild ? "production" : "development", | ||||
|                     ), | ||||
|                     "process.env.CWD": JSON.stringify(cwd()), | ||||
|                     "process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath), | ||||
|                     "preventAssignment": true, | ||||
|                 }), | ||||
|                 ...config.plugins, | ||||
|                 postcssLit(), | ||||
|                 tsconfigPaths(), | ||||
|             ], | ||||
|         }; | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| export default config; | ||||
							
								
								
									
										38
									
								
								web/.storybook/manager.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								web/.storybook/manager.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| /** | ||||
|  * @file Storybook manager configuration. | ||||
|  * | ||||
|  * @import { ThemeVarsPartial } from "storybook/internal/theming"; | ||||
|  */ | ||||
| import { createUIThemeEffect, resolveUITheme } from "@goauthentik/web/common/theme.ts"; | ||||
| import { addons } from "@storybook/manager-api"; | ||||
| import { create } from "@storybook/theming/create"; | ||||
|  | ||||
| /** | ||||
|  * @satisfies {Partial<ThemeVarsPartial>} | ||||
|  */ | ||||
| const baseTheme = { | ||||
|     brandTitle: "authentik Storybook", | ||||
|     brandUrl: "https://goauthentik.io", | ||||
|     brandImage: "https://goauthentik.io/img/icon_left_brand_colour.svg", | ||||
|     brandTarget: "_self", | ||||
| }; | ||||
|  | ||||
| const uiTheme = resolveUITheme(); | ||||
|  | ||||
| addons.setConfig({ | ||||
|     theme: create({ | ||||
|         ...baseTheme, | ||||
|         base: uiTheme, | ||||
|     }), | ||||
|     enableShortcuts: false, | ||||
| }); | ||||
|  | ||||
| createUIThemeEffect((nextUITheme) => { | ||||
|     addons.setConfig({ | ||||
|         theme: create({ | ||||
|             ...baseTheme, | ||||
|             base: nextUITheme, | ||||
|         }), | ||||
|         enableShortcuts: false, | ||||
|     }); | ||||
| }); | ||||
| @ -1,9 +0,0 @@ | ||||
| // .storybook/manager.js | ||||
| import { addons } from "@storybook/manager-api"; | ||||
|  | ||||
| import authentikTheme from "./authentikTheme"; | ||||
|  | ||||
| addons.setConfig({ | ||||
|     theme: authentikTheme, | ||||
|     enableShortcuts: false, | ||||
| }); | ||||
| @ -1,5 +1,3 @@ | ||||
| <link rel="stylesheet" href="@patternfly/patternfly/patternfly-base.css" /> | ||||
| <link rel="stylesheet" href="@goauthentik/common/styles/authentik.css" /> | ||||
| <style> | ||||
|     body { | ||||
|         overflow-y: scroll; | ||||
|  | ||||
							
								
								
									
										32
									
								
								web/.storybook/preview.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								web/.storybook/preview.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| /// <reference types="../types/css.js" /> | ||||
| /** | ||||
|  * @file Storybook manager configuration. | ||||
|  * | ||||
|  * @import { Preview } from "@storybook/web-components"; | ||||
|  */ | ||||
| import { applyDocumentTheme } from "@goauthentik/web/common/theme.ts"; | ||||
|  | ||||
| applyDocumentTheme(); | ||||
|  | ||||
| /** | ||||
|  * @satisfies {Preview} | ||||
|  */ | ||||
| const preview = { | ||||
|     parameters: { | ||||
|         options: { | ||||
|             storySort: { | ||||
|                 method: "alphabetical", | ||||
|             }, | ||||
|         }, | ||||
|         actions: { argTypesRegex: "^on[A-Z].*" }, | ||||
|  | ||||
|         controls: { | ||||
|             matchers: { | ||||
|                 color: /(background|color)$/i, | ||||
|                 date: /Date$/, | ||||
|             }, | ||||
|         }, | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| export default preview; | ||||
| @ -1,30 +0,0 @@ | ||||
| import type { Preview } from "@storybook/web-components"; | ||||
|  | ||||
| import "@goauthentik/common/styles/authentik.css"; | ||||
| // import "@goauthentik/common/styles/theme-dark.css"; | ||||
| import "@patternfly/patternfly/components/Brand/brand.css"; | ||||
| import "@patternfly/patternfly/components/Page/page.css"; | ||||
| // .storybook/preview.js | ||||
| import "@patternfly/patternfly/patternfly-base.css"; | ||||
|  | ||||
| const preview: Preview = { | ||||
|     parameters: { | ||||
|         options: { | ||||
|             storySort: { | ||||
|                 method: "alphabetical", | ||||
|             }, | ||||
|         }, | ||||
|         actions: { argTypesRegex: "^on[A-Z].*" }, | ||||
|         cssUserPrefs: { | ||||
|             "prefers-color-scheme": "light", | ||||
|         }, | ||||
|         controls: { | ||||
|             matchers: { | ||||
|                 color: /(background|color)$/i, | ||||
|                 date: /Date$/, | ||||
|             }, | ||||
|         }, | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| export default preview; | ||||
							
								
								
									
										3519
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3519
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										140
									
								
								web/package.json
									
									
									
									
									
								
							
							
						
						
									
										140
									
								
								web/package.json
									
									
									
									
									
								
							| @ -1,6 +1,50 @@ | ||||
| { | ||||
|     "name": "@goauthentik/web", | ||||
|     "version": "0.0.0", | ||||
|     "license": "MIT", | ||||
|     "private": true, | ||||
|     "scripts": { | ||||
|         "build": "wireit", | ||||
|         "build-locales": "wireit", | ||||
|         "build-locales:build": "wireit", | ||||
|         "build-proxy": "wireit", | ||||
|         "build:sfe": "wireit", | ||||
|         "esbuild:watch": "node scripts/build-web.mjs --watch", | ||||
|         "extract-locales": "wireit", | ||||
|         "format": "wireit", | ||||
|         "lint": "wireit", | ||||
|         "lint:imports": "wireit", | ||||
|         "lint:lockfile": "wireit", | ||||
|         "lint:nightmare": "wireit", | ||||
|         "lint:precommit": "wireit", | ||||
|         "lint:types": "wireit", | ||||
|         "lit-analyse": "wireit", | ||||
|         "precommit": "wireit", | ||||
|         "prettier": "wireit", | ||||
|         "prettier-check": "wireit", | ||||
|         "pseudolocalize": "wireit", | ||||
|         "storybook": "storybook dev -p 6006", | ||||
|         "storybook:build": "wireit", | ||||
|         "test": "wireit", | ||||
|         "test:e2e": "wireit", | ||||
|         "test:e2e:watch": "wireit", | ||||
|         "test:watch": "wireit", | ||||
|         "tsc": "wireit", | ||||
|         "watch": "run-s build-locales esbuild:watch" | ||||
|     }, | ||||
|     "type": "module", | ||||
|     "exports": { | ||||
|         "./package.json": "./package.json", | ||||
|         "./paths": "./paths.js", | ||||
|         "./scripts/*": "./scripts/*.mjs", | ||||
|         "./elements/*": "./src/elements/*", | ||||
|         "./common/*": "./src/common/*", | ||||
|         "./components/*": "./src/components/*", | ||||
|         "./flow/*": "./src/flow/*", | ||||
|         "./locales/*": "./src/locales/*", | ||||
|         "./user/*": "./src/user/*", | ||||
|         "./admin/*": "./src/admin/*" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@codemirror/lang-css": "^6.3.1", | ||||
|         "@codemirror/lang-html": "^6.4.9", | ||||
| @ -12,8 +56,7 @@ | ||||
|         "@floating-ui/dom": "^1.6.11", | ||||
|         "@formatjs/intl-listformat": "^7.5.7", | ||||
|         "@fortawesome/fontawesome-free": "^6.6.0", | ||||
|         "@goauthentik/api": "^2025.2.4-1745519715", | ||||
|         "@lit-labs/ssr": "3.2.2", | ||||
|         "@goauthentik/api": "^2025.4.1-1747332783", | ||||
|         "@lit/context": "^1.1.2", | ||||
|         "@lit/localize": "^0.12.2", | ||||
|         "@lit/reactive-element": "^2.0.4", | ||||
| @ -54,6 +97,7 @@ | ||||
|         "remark-gfm": "^4.0.1", | ||||
|         "remark-mdx-frontmatter": "^5.0.0", | ||||
|         "style-mod": "^4.1.2", | ||||
|         "trusted-types": "^2.0.0", | ||||
|         "ts-pattern": "^5.4.0", | ||||
|         "unist-util-visit": "^5.0.0", | ||||
|         "webcomponent-qr-code": "^1.2.0", | ||||
| @ -62,19 +106,20 @@ | ||||
|     "devDependencies": { | ||||
|         "@eslint/js": "^9.11.1", | ||||
|         "@goauthentik/esbuild-plugin-live-reload": "^1.0.4", | ||||
|         "@goauthentik/monorepo": "^1.0.0", | ||||
|         "@goauthentik/prettier-config": "^1.0.4", | ||||
|         "@goauthentik/tsconfig": "^1.0.4", | ||||
|         "@hcaptcha/types": "^1.0.4", | ||||
|         "@lit/localize-tools": "^0.8.0", | ||||
|         "@rollup/plugin-replace": "^6.0.1", | ||||
|         "@storybook/addon-essentials": "^8.3.4", | ||||
|         "@storybook/addon-links": "^8.3.4", | ||||
|         "@storybook/api": "^7.6.17", | ||||
|         "@storybook/blocks": "^8.3.4", | ||||
|         "@storybook/builder-vite": "^8.3.4", | ||||
|         "@storybook/manager-api": "^8.3.4", | ||||
|         "@storybook/web-components": "^8.3.4", | ||||
|         "@storybook/web-components-vite": "^8.3.4", | ||||
|         "@storybook/addon-essentials": "^8.6.12", | ||||
|         "@storybook/addon-links": "^8.6.12", | ||||
|         "@storybook/blocks": "^8.6.12", | ||||
|         "@storybook/experimental-addon-test": "^8.6.12", | ||||
|         "@storybook/manager-api": "^8.6.12", | ||||
|         "@storybook/test": "^8.6.12", | ||||
|         "@storybook/web-components": "^8.6.12", | ||||
|         "@storybook/web-components-vite": "^8.6.12", | ||||
|         "@trivago/prettier-plugin-sort-imports": "^5.2.2", | ||||
|         "@types/chart.js": "^2.9.41", | ||||
|         "@types/codemirror": "^5.60.15", | ||||
| @ -93,24 +138,22 @@ | ||||
|         "@wdio/spec-reporter": "^9.1.2", | ||||
|         "chromedriver": "^131.0.1", | ||||
|         "esbuild": "^0.25.0", | ||||
|         "esbuild-plugin-copy": "^2.1.1", | ||||
|         "esbuild-plugin-polyfill-node": "^0.3.0", | ||||
|         "esbuild-plugins-node-modules-polyfill": "^1.7.0", | ||||
|         "eslint": "^9.11.1", | ||||
|         "eslint-plugin-lit": "^1.15.0", | ||||
|         "eslint-plugin-wc": "^2.1.1", | ||||
|         "github-slugger": "^2.0.0", | ||||
|         "glob": "^11.0.0", | ||||
|         "globals": "^15.10.0", | ||||
|         "knip": "^5.30.6", | ||||
|         "lit-analyzer": "^2.0.3", | ||||
|         "npm-run-all": "^4.1.5", | ||||
|         "prettier": "^3.3.3", | ||||
|         "pseudolocale": "^2.1.0", | ||||
|         "rollup-plugin-modify": "^3.0.0", | ||||
|         "rollup-plugin-postcss-lit": "^2.1.0", | ||||
|         "storybook": "^8.3.4", | ||||
|         "storybook": "^8.6.12", | ||||
|         "storybook-addon-mock": "^5.0.0", | ||||
|         "syncpack": "^13.0.0", | ||||
|         "turnstile-types": "^1.2.3", | ||||
|         "typescript": "^5.6.2", | ||||
|         "typescript-eslint": "^8.8.0", | ||||
| @ -118,10 +161,6 @@ | ||||
|         "vite-tsconfig-paths": "^5.0.1", | ||||
|         "wireit": "^0.14.9" | ||||
|     }, | ||||
|     "engines": { | ||||
|         "node": ">=20" | ||||
|     }, | ||||
|     "license": "MIT", | ||||
|     "optionalDependencies": { | ||||
|         "@esbuild/darwin-arm64": "^0.24.0", | ||||
|         "@esbuild/linux-amd64": "^0.18.11", | ||||
| @ -130,48 +169,6 @@ | ||||
|         "@rollup/rollup-linux-arm64-gnu": "4.23.0", | ||||
|         "@rollup/rollup-linux-x64-gnu": "4.23.0" | ||||
|     }, | ||||
|     "overrides": { | ||||
|         "rapidoc": { | ||||
|             "@apitools/openapi-parser@": "0.0.37" | ||||
|         }, | ||||
|         "chromedriver": { | ||||
|             "axios": "^1.8.4" | ||||
|         } | ||||
|     }, | ||||
|     "prettier": "@goauthentik/prettier-config", | ||||
|     "private": true, | ||||
|     "scripts": { | ||||
|         "build": "wireit", | ||||
|         "build-locales": "wireit", | ||||
|         "build-locales:build": "wireit", | ||||
|         "build-proxy": "wireit", | ||||
|         "build:sfe": "wireit", | ||||
|         "esbuild:watch": "node scripts/build-web.mjs --watch", | ||||
|         "extract-locales": "wireit", | ||||
|         "format": "wireit", | ||||
|         "lint": "wireit", | ||||
|         "lint:imports": "wireit", | ||||
|         "lint:lockfile": "wireit", | ||||
|         "lint:nightmare": "wireit", | ||||
|         "lint:package": "wireit", | ||||
|         "lint:precommit": "wireit", | ||||
|         "lint:types": "wireit", | ||||
|         "lit-analyse": "wireit", | ||||
|         "postinstall": "bash scripts/patch-spotlight.sh", | ||||
|         "precommit": "wireit", | ||||
|         "prettier": "wireit", | ||||
|         "prettier-check": "wireit", | ||||
|         "pseudolocalize": "wireit", | ||||
|         "storybook": "storybook dev -p 6006", | ||||
|         "storybook:build": "wireit", | ||||
|         "test": "wireit", | ||||
|         "test:e2e": "wireit", | ||||
|         "test:e2e:watch": "wireit", | ||||
|         "test:watch": "wireit", | ||||
|         "tsc": "wireit", | ||||
|         "watch": "run-s build-locales esbuild:watch" | ||||
|     }, | ||||
|     "type": "module", | ||||
|     "wireit": { | ||||
|         "build": { | ||||
|             "#comment": [ | ||||
| @ -248,10 +245,7 @@ | ||||
|             "command": "lit-localize extract" | ||||
|         }, | ||||
|         "format": { | ||||
|             "command": "prettier --write .", | ||||
|             "dependencies": [ | ||||
|                 "lint:package" | ||||
|             ] | ||||
|             "command": "prettier --write ." | ||||
|         }, | ||||
|         "format:packages": { | ||||
|             "dependencies": [ | ||||
| @ -290,9 +284,6 @@ | ||||
|                 "./packages/sfe:lint:lockfile" | ||||
|             ] | ||||
|         }, | ||||
|         "lint:package": { | ||||
|             "command": "syncpack format -i '    '" | ||||
|         }, | ||||
|         "lint:nightmare": { | ||||
|             "command": "${NODE_RUNNER} ./scripts/eslint.mjs --nightmare", | ||||
|             "env": { | ||||
| @ -323,7 +314,6 @@ | ||||
|                 "lint:types", | ||||
|                 "lint:components", | ||||
|                 "lint:spelling", | ||||
|                 "lint:package", | ||||
|                 "lint:lockfile", | ||||
|                 "lint:lockfiles", | ||||
|                 "lint:precommit", | ||||
| @ -388,8 +378,20 @@ | ||||
|             ] | ||||
|         } | ||||
|     }, | ||||
|     "engines": { | ||||
|         "node": ">=20" | ||||
|     }, | ||||
|     "workspaces": [ | ||||
|         ".", | ||||
|         "./packages/*" | ||||
|     ] | ||||
|     ], | ||||
|     "prettier": "@goauthentik/prettier-config", | ||||
|     "overrides": { | ||||
|         "rapidoc": { | ||||
|             "@apitools/openapi-parser@": "0.0.37" | ||||
|         }, | ||||
|         "chromedriver": { | ||||
|             "axios": "^1.8.4" | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -6,7 +6,7 @@ | ||||
|  * @import { Message as ESBuildMessage } from "esbuild"; | ||||
|  */ | ||||
|  | ||||
| const logPrefix = "👷 [ESBuild]"; | ||||
| const logPrefix = "authentik/dev/web: "; | ||||
| const log = console.debug.bind(console, logPrefix); | ||||
|  | ||||
| /** | ||||
| @ -21,7 +21,7 @@ const log = console.debug.bind(console, logPrefix); | ||||
|  * ESBuild may tree-shake it out of production builds. | ||||
|  * | ||||
|  * ```ts | ||||
|  * if (process.env.NODE_ENV === "development") { | ||||
|  * if (import.meta.env.NODE_ENV=== "development") { | ||||
|  *   await import("@goauthentik/esbuild-plugin-live-reload/client") | ||||
|  *     .catch(() => console.warn("Failed to import watcher")) | ||||
|  * } | ||||
| @ -76,7 +76,7 @@ export class ESBuildObserver extends EventSource { | ||||
|      */ | ||||
|     #startListener = () => { | ||||
|         this.#trackActivity(); | ||||
|         log("⏰  Build started..."); | ||||
|         log("⏰ Build started..."); | ||||
|     }; | ||||
|  | ||||
|     #internalErrorListener = () => { | ||||
| @ -86,7 +86,7 @@ export class ESBuildObserver extends EventSource { | ||||
|             clearTimeout(this.#keepAliveInterval); | ||||
|  | ||||
|             this.close(); | ||||
|             log("⛔️  Closing connection"); | ||||
|             log("⛔️ Closing connection"); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
| @ -126,13 +126,13 @@ export class ESBuildObserver extends EventSource { | ||||
|         this.#trackActivity(); | ||||
|  | ||||
|         if (!this.online) { | ||||
|             log("🚫  Build finished while offline."); | ||||
|             log("🚫 Build finished while offline."); | ||||
|             this.deferredReload = true; | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         log("🛎️  Build completed! Reloading..."); | ||||
|         log("🛎️ Build completed! Reloading..."); | ||||
|  | ||||
|         // We use an animation frame to keep the reload from happening before the | ||||
|         // event loop has a chance to process the message. | ||||
| @ -189,13 +189,13 @@ export class ESBuildObserver extends EventSource { | ||||
|  | ||||
|             if (!this.deferredReload) return; | ||||
|  | ||||
|             log("🛎️  Reloading after offline build..."); | ||||
|             log("🛎️ Reloading after offline build..."); | ||||
|             this.deferredReload = false; | ||||
|  | ||||
|             window.location.reload(); | ||||
|         }); | ||||
|  | ||||
|         log("🛎️  Listening for build changes..."); | ||||
|         log("🛎️ Listening for build changes..."); | ||||
|  | ||||
|         this.#keepAliveInterval = setInterval(() => { | ||||
|             const now = Date.now(); | ||||
| @ -203,7 +203,7 @@ export class ESBuildObserver extends EventSource { | ||||
|             if (now - this.lastUpdatedAt < 10_000) return; | ||||
|  | ||||
|             this.alive = false; | ||||
|             log("👋  Waiting for build to start..."); | ||||
|             log("👋 Waiting for build to start..."); | ||||
|         }, 15_000); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -4,15 +4,20 @@ | ||||
|  | ||||
| export {}; | ||||
| declare global { | ||||
|     /** | ||||
|      * Environment variables injected by ESBuild. | ||||
|      */ | ||||
|     interface ImportMetaEnv { | ||||
|         /** | ||||
|          * The injected watcher URL for ESBuild. | ||||
|          * This is used for live reloading in development mode. | ||||
|          * | ||||
|          * @format url | ||||
|          */ | ||||
|         readonly ESBUILD_WATCHER_URL?: string; | ||||
|     } | ||||
|  | ||||
|     interface ImportMeta { | ||||
|         readonly env: { | ||||
|             /** | ||||
|              * The injected watcher URL for ESBuild. | ||||
|              * This is used for live reloading in development mode. | ||||
|              * | ||||
|              * @format url | ||||
|              */ | ||||
|             ESBUILD_WATCHER_URL: string; | ||||
|         }; | ||||
|         readonly env: ImportMetaEnv; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,22 +1,11 @@ | ||||
| { | ||||
|     "name": "@goauthentik/esbuild-plugin-live-reload", | ||||
|     "description": "ESBuild plugin to watch for file changes and trigger client-side reloads.", | ||||
|     "version": "1.0.4", | ||||
|     "dependencies": { | ||||
|         "find-free-ports": "^3.1.1" | ||||
|     }, | ||||
|     "devDependencies": { | ||||
|         "@goauthentik/prettier-config": "^1.0.4", | ||||
|         "@goauthentik/tsconfig": "^1.0.4", | ||||
|         "@trivago/prettier-plugin-sort-imports": "^5.2.2", | ||||
|         "@types/node": "^22.14.1", | ||||
|         "esbuild": "^0.25.0", | ||||
|         "prettier": "^3.3.3", | ||||
|         "typescript": "^5.6.2" | ||||
|     }, | ||||
|     "engines": { | ||||
|         "node": ">=20.11" | ||||
|     }, | ||||
|     "description": "ESBuild plugin to watch for file changes and trigger client-side reloads.", | ||||
|     "license": "MIT", | ||||
|     "private": true, | ||||
|     "main": "index.js", | ||||
|     "type": "module", | ||||
|     "exports": { | ||||
|         "./package.json": "./package.json", | ||||
|         ".": { | ||||
| @ -32,22 +21,33 @@ | ||||
|             "import": "./plugin/index.js" | ||||
|         } | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "find-free-ports": "^3.1.1" | ||||
|     }, | ||||
|     "devDependencies": { | ||||
|         "@goauthentik/prettier-config": "^1.0.4", | ||||
|         "@goauthentik/tsconfig": "^1.0.4", | ||||
|         "@trivago/prettier-plugin-sort-imports": "^5.2.2", | ||||
|         "@types/node": "^22.14.1", | ||||
|         "esbuild": "^0.25.0", | ||||
|         "prettier": "^3.3.3", | ||||
|         "typescript": "^5.6.2" | ||||
|     }, | ||||
|     "peerDependencies": { | ||||
|         "esbuild": "^0.25.0" | ||||
|     }, | ||||
|     "engines": { | ||||
|         "node": ">=20.11" | ||||
|     }, | ||||
|     "types": "./out/index.d.ts", | ||||
|     "files": [ | ||||
|         "./index.js", | ||||
|         "client/**/*", | ||||
|         "plugin/**/*", | ||||
|         "out/**/*" | ||||
|     ], | ||||
|     "license": "MIT", | ||||
|     "main": "index.js", | ||||
|     "peerDependencies": { | ||||
|         "esbuild": "^0.25.0" | ||||
|     }, | ||||
|     "prettier": "@goauthentik/prettier-config", | ||||
|     "private": true, | ||||
|     "publishConfig": { | ||||
|         "access": "public" | ||||
|     }, | ||||
|     "type": "module", | ||||
|     "types": "./out/index.d.ts" | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -2,4 +2,3 @@ | ||||
| 
 | ||||
| This package contains utility scripts common to all TypeScript and JavaScript packages in the | ||||
| `@goauthentik` monorepo. | ||||
| 
 | ||||
							
								
								
									
										65
									
								
								web/packages/monorepo/environment.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								web/packages/monorepo/environment.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | ||||
| /** | ||||
|  * @file Utility functions for working with environment variables. | ||||
|  */ | ||||
| /// <reference types="./types/global.js" /> | ||||
|  | ||||
| //#region Constants | ||||
|  | ||||
| /** | ||||
|  * The current Node.js environment, defaulting to "development" when not set. | ||||
|  * | ||||
|  * Note, this should only be used during the build process. | ||||
|  * | ||||
|  * If you need to check the environment at runtime, use `process.env.NODE_ENV` to | ||||
|  * ensure that module tree-shaking works correctly. | ||||
|  * | ||||
|  */ | ||||
| export const NodeEnvironment = process.env.NODE_ENV || "development"; | ||||
|  | ||||
| /** | ||||
|  * A source environment variable, which can be a string, number, boolean, null, or undefined. | ||||
|  * @typedef {string | number | boolean | null | undefined} EnvironmentVariable | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * A type helper for serializing environment variables. | ||||
|  * | ||||
|  * @template {EnvironmentVariable} T | ||||
|  * @typedef {T extends string ? `"${T}"` : T} JSONify | ||||
|  */ | ||||
|  | ||||
| //#endregion | ||||
|  | ||||
| //#region Utilities | ||||
|  | ||||
| /** | ||||
|  * Given an object of environment variables, returns a new object with the same keys and values, but | ||||
|  * with the values serialized as strings. | ||||
|  * | ||||
|  * @template {Record<string, EnvironmentVariable>} EnvRecord | ||||
|  * @template {string} [Prefix='import.meta.env.'] | ||||
|  * | ||||
|  * @param {EnvRecord} input | ||||
|  * @param {Prefix} [prefix='import.meta.env.'] | ||||
|  * | ||||
|  * @returns {{[K in keyof EnvRecord as `${Prefix}${K}`]: JSONify<EnvRecord[K]>}} | ||||
|  */ | ||||
| export function serializeEnvironmentVars( | ||||
|     input, | ||||
|     prefix = /** @type {Prefix} */ ("import.meta.env."), | ||||
| ) { | ||||
|     /** | ||||
|      * @type {Record<string, string>} | ||||
|      */ | ||||
|     const env = {}; | ||||
|  | ||||
|     for (const [key, value] of Object.entries(input)) { | ||||
|         const namespaceKey = prefix + key; | ||||
|  | ||||
|         env[namespaceKey] = JSON.stringify(value || ""); | ||||
|     } | ||||
|  | ||||
|     return /** @type {any} */ (env); | ||||
| } | ||||
|  | ||||
| //#endregion | ||||
| @ -1,4 +1,6 @@ | ||||
| /// <reference types="./types/global.js" />
 | ||||
| 
 | ||||
| export * from "./paths.js"; | ||||
| export * from "./constants.js"; | ||||
| export * from "./environment.js"; | ||||
| export * from "./version.js"; | ||||
| export * from "./scripting.js"; | ||||
							
								
								
									
										28
									
								
								web/packages/monorepo/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								web/packages/monorepo/package.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| { | ||||
|     "name": "@goauthentik/monorepo", | ||||
|     "version": "1.0.0", | ||||
|     "description": "Utilities for the authentik monorepo.", | ||||
|     "license": "MIT", | ||||
|     "private": true, | ||||
|     "main": "index.js", | ||||
|     "type": "module", | ||||
|     "exports": { | ||||
|         "./package.json": "./package.json", | ||||
|         ".": { | ||||
|             "types": "./out/index.d.ts", | ||||
|             "import": "./index.js" | ||||
|         } | ||||
|     }, | ||||
|     "devDependencies": { | ||||
|         "@goauthentik/prettier-config": "^1.0.4", | ||||
|         "@goauthentik/tsconfig": "^1.0.4", | ||||
|         "@types/node": "^22.14.1", | ||||
|         "prettier": "^3.3.3", | ||||
|         "typescript": "^5.6.2" | ||||
|     }, | ||||
|     "engines": { | ||||
|         "node": ">=20.11" | ||||
|     }, | ||||
|     "types": "./out/index.d.ts", | ||||
|     "prettier": "@goauthentik/prettier-config" | ||||
| } | ||||
							
								
								
									
										45
									
								
								web/packages/monorepo/paths.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								web/packages/monorepo/paths.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | ||||
| import { createRequire } from "node:module"; | ||||
| import { dirname, join, resolve } from "node:path"; | ||||
| import { fileURLToPath } from "node:url"; | ||||
|  | ||||
| const relativeDirname = dirname(fileURLToPath(import.meta.url)); | ||||
|  | ||||
| /** | ||||
|  * @typedef {'~authentik'} MonoRepoRoot | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * The root of the authentik monorepo. | ||||
|  */ | ||||
| // TODO: Revise when this package is moved to the monorepo's `packages/monorepo` directory. | ||||
| export const MonoRepoRoot = /** @type {MonoRepoRoot} */ ( | ||||
|     resolve(relativeDirname, "..", "..", "..") | ||||
| ); | ||||
|  | ||||
| const require = createRequire(import.meta.url); | ||||
|  | ||||
| /** | ||||
|  * Resolve a package name to its location in the monorepo to the single node_modules directory. | ||||
|  * @param {string} packageName | ||||
|  * | ||||
|  * @returns {string} The resolved path to the package. | ||||
|  * @throws {Error} If the package cannot be resolved. | ||||
|  */ | ||||
| export function resolvePackage(packageName) { | ||||
|     const relativePackageJSONPath = join(packageName, "package.json"); | ||||
|  | ||||
|     /** @type {string} */ | ||||
|     let absolutePackageJSONPath; | ||||
|  | ||||
|     try { | ||||
|         absolutePackageJSONPath = require.resolve(relativePackageJSONPath); | ||||
|     } catch (cause) { | ||||
|         const error = new Error(`Failed to resolve package "${packageName}"`); | ||||
|  | ||||
|         error.cause = cause; | ||||
|  | ||||
|         throw error; | ||||
|     } | ||||
|  | ||||
|     return dirname(absolutePackageJSONPath); | ||||
| } | ||||
							
								
								
									
										15
									
								
								web/packages/monorepo/types/global.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								web/packages/monorepo/types/global.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| declare module "process" { | ||||
|     global { | ||||
|         namespace NodeJS { | ||||
|             interface ProcessEnv { | ||||
|                 /** | ||||
|                  * An environment variable used to determine | ||||
|                  * whether Node.js is running in production mode. | ||||
|                  * | ||||
|                  * @see {@link https://nodejs.org/en/learn/getting-started/nodejs-the-difference-between-development-and-production | The difference between development and production} | ||||
|                  */ | ||||
|                 NODE_ENV?: "production" | "development"; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,6 +1,6 @@ | ||||
| import { execSync } from "node:child_process"; | ||||
| 
 | ||||
| import PackageJSON from "../../package.json" with { type: "json" }; | ||||
| import PackageJSON from "../../../package.json" with { type: "json" }; | ||||
| import { MonoRepoRoot } from "./paths.js"; | ||||
| 
 | ||||
| /** | ||||
| @ -210,6 +210,9 @@ class PasswordStage extends Stage<PasswordChallenge> { | ||||
|             <form id="password-form"> | ||||
|                 <img class="mb-4 brand-icon" src="${ak().brand.branding_logo}" alt=""> | ||||
|                 <h1 class="h3 mb-3 fw-normal text-center">${this.challenge?.flowInfo?.title}</h1> | ||||
|                 <div class="form-label-group my-3"> | ||||
|                     <input type="text" readonly class="form-control-plaintext" value="Welcome, ${this.challenge?.pendingUser}."> | ||||
|                 </div> | ||||
|                 <div class="form-label-group my-3 has-validation"> | ||||
|                     <input type="password" autofocus class="form-control ${this.error("password").length > 0 ? IS_INVALID : ""}" name="password" placeholder="Password"> | ||||
|                     ${this.renderInputError("password")} | ||||
|  | ||||
							
								
								
									
										78
									
								
								web/paths.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								web/paths.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | ||||
| import { dirname, resolve } from "node:path"; | ||||
| import { fileURLToPath } from "node:url"; | ||||
|  | ||||
| const relativeDirname = dirname(fileURLToPath(import.meta.url)); | ||||
|  | ||||
| //#region Base paths | ||||
|  | ||||
| /** | ||||
|  * @typedef {'@goauthentik/web'} WebPackageIdentifier | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * The root of the web package. | ||||
|  */ | ||||
| export const PackageRoot = /** @type {WebPackageIdentifier} */ (resolve(relativeDirname)); | ||||
|  | ||||
| /** | ||||
|  * The name of the distribution directory. | ||||
|  */ | ||||
| export const DistDirectoryName = "dist"; | ||||
|  | ||||
| /** | ||||
|  * Path to the web package's distribution directory. | ||||
|  * | ||||
|  * This is where the built files are located after running the build process. | ||||
|  */ | ||||
| export const DistDirectory = /** @type {`${WebPackageIdentifier}/${DistDirectoryName}`} */ ( | ||||
|     resolve(relativeDirname, DistDirectoryName) | ||||
| ); | ||||
|  | ||||
| //#endregion | ||||
|  | ||||
| //#region Entry points | ||||
|  | ||||
| /** | ||||
|  * @typedef {{ in: string, out: string }} EntryPointTarget | ||||
|  * | ||||
|  * ESBuild entrypoint target. | ||||
|  * Matches the type defined in the ESBuild context. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Entry points available for building. | ||||
|  * | ||||
|  * @satisfies {Record<string, EntryPointTarget>} | ||||
|  */ | ||||
| export const EntryPoint = /** @type {const} */ ({ | ||||
|     Admin: { | ||||
|         in: resolve(PackageRoot, "src", "admin", "AdminInterface", "index.entrypoint.ts"), | ||||
|         out: resolve(DistDirectory, "admin", "AdminInterface"), | ||||
|     }, | ||||
|     User: { | ||||
|         in: resolve(PackageRoot, "src", "user", "index.entrypoint.ts"), | ||||
|         out: resolve(DistDirectory, "user", "UserInterface"), | ||||
|     }, | ||||
|     Flow: { | ||||
|         in: resolve(PackageRoot, "src", "flow", "index.entrypoint.ts"), | ||||
|         out: resolve(DistDirectory, "flow", "FlowInterface"), | ||||
|     }, | ||||
|     Standalone: { | ||||
|         in: resolve(PackageRoot, "src", "standalone", "api-browser/index.entrypoint.ts"), | ||||
|         out: resolve(DistDirectory, "standalone", "api-browser", "index"), | ||||
|     }, | ||||
|     StandaloneLoading: { | ||||
|         in: resolve(PackageRoot, "src", "standalone", "loading/index.entrypoint.ts"), | ||||
|         out: resolve(DistDirectory, "standalone", "loading", "index"), | ||||
|     }, | ||||
|     RAC: { | ||||
|         in: resolve(PackageRoot, "src", "rac", "index.entrypoint.ts"), | ||||
|         out: resolve(DistDirectory, "rac", "index"), | ||||
|     }, | ||||
|     Polyfill: { | ||||
|         in: resolve(PackageRoot, "src", "polyfill", "index.entrypoint.ts"), | ||||
|         out: resolve(DistDirectory, "poly"), | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| //#endregion | ||||
| @ -1,17 +1,32 @@ | ||||
| import { spawnSync } from "child_process"; | ||||
| import fs from "fs"; | ||||
| import path from "path"; | ||||
| import process from "process"; | ||||
| /** | ||||
|  * @file Lit Localize build script. | ||||
|  * | ||||
|  * @remarks | ||||
|  * Determines if all the Xliff translation source files are present and if the Typescript source files generated from those sources are up-to-date. | ||||
|  * | ||||
|  * If they are not, it runs the locale building script, | ||||
|  * intercepting the long spew of "this string is not translated" and replacing it with a | ||||
|  * summary of how many strings are missing with respect to the source locale. | ||||
|  * | ||||
|  * @import { ConfigFile } from "@lit/localize-tools/lib/types/config" | ||||
|  */ | ||||
| import { PackageRoot } from "@goauthentik/web/paths"; | ||||
| import { spawnSync } from "node:child_process"; | ||||
| import { readFileSync, statSync } from "node:fs"; | ||||
| import path from "node:path"; | ||||
|  | ||||
| /** | ||||
|  * Determines if all the Xliff translation source files are present and if the Typescript source | ||||
|  * files generated from those sources are up-to-date. If they are not, it runs the locale building | ||||
|  * script, intercepting the long spew of "this string is not translated" and replacing it with a | ||||
|  * summary of how many strings are missing with respect to the source locale. | ||||
|  * @type {ConfigFile} | ||||
|  */ | ||||
| const localizeRules = JSON.parse( | ||||
|     readFileSync(path.join(PackageRoot, "lit-localize.json"), "utf-8"), | ||||
| ); | ||||
|  | ||||
| const localizeRules = JSON.parse(fs.readFileSync("./lit-localize.json", "utf-8")); | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @param {string} loc | ||||
|  * @returns {boolean} | ||||
|  */ | ||||
| function generatedFileIsUpToDateWithXliffSource(loc) { | ||||
|     const xliff = path.join("./xliff", `${loc}.xlf`); | ||||
|     const gened = path.join("./src/locales", `${loc}.ts`); | ||||
| @ -22,7 +37,7 @@ function generatedFileIsUpToDateWithXliffSource(loc) { | ||||
|     // generates a unique error message and halts the build. | ||||
|  | ||||
|     try { | ||||
|         var xlfStat = fs.statSync(xliff); | ||||
|         var xlfStat = statSync(xliff); | ||||
|     } catch (_error) { | ||||
|         console.error(`lit-localize expected '${loc}.xlf', but XLF file is not present`); | ||||
|         process.exit(1); | ||||
| @ -30,7 +45,7 @@ function generatedFileIsUpToDateWithXliffSource(loc) { | ||||
|  | ||||
|     // If the generated file doesn't exist, of course it's not up to date. | ||||
|     try { | ||||
|         var genedStat = fs.statSync(gened); | ||||
|         var genedStat = statSync(gened); | ||||
|     } catch (_error) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
| @ -1,144 +1,87 @@ | ||||
| /// <reference types="../types/esbuild.js" /> | ||||
| /** | ||||
|  * @file ESBuild script for building the authentik web UI. | ||||
|  * | ||||
|  * @import { BuildOptions } from "esbuild"; | ||||
|  */ | ||||
| import { liveReloadPlugin } from "@goauthentik/esbuild-plugin-live-reload/plugin"; | ||||
| import { execFileSync } from "child_process"; | ||||
| import { | ||||
|     MonoRepoRoot, | ||||
|     NodeEnvironment, | ||||
|     readBuildIdentifier, | ||||
|     resolvePackage, | ||||
| } from "@goauthentik/monorepo"; | ||||
| import { DistDirectory, DistDirectoryName, EntryPoint, PackageRoot } from "@goauthentik/web/paths"; | ||||
| import { deepmerge } from "deepmerge-ts"; | ||||
| import esbuild from "esbuild"; | ||||
| import copy from "esbuild-plugin-copy"; | ||||
| import { polyfillNode } from "esbuild-plugin-polyfill-node"; | ||||
| import { copyFileSync, mkdirSync, readFileSync, statSync } from "fs"; | ||||
| import { globSync } from "glob"; | ||||
| import * as path from "path"; | ||||
| import { cwd } from "process"; | ||||
| import process from "process"; | ||||
| import { fileURLToPath } from "url"; | ||||
| import * as fs from "node:fs/promises"; | ||||
| import * as path from "node:path"; | ||||
|  | ||||
| import { mdxPlugin } from "./esbuild/build-mdx-plugin.mjs"; | ||||
| import { createBundleDefinitions } from "./esbuild/environment.mjs"; | ||||
|  | ||||
| const __dirname = fileURLToPath(new URL(".", import.meta.url)); | ||||
| let authentikProjectRoot = path.join(__dirname, "..", ".."); | ||||
| const logPrefix = "[Build]"; | ||||
|  | ||||
| try { | ||||
|     // Use the package.json file in the root folder, as it has the current version information. | ||||
|     authentikProjectRoot = execFileSync("git", ["rev-parse", "--show-toplevel"], { | ||||
|         encoding: "utf8", | ||||
|     }).replace("\n", ""); | ||||
| } catch (_error) { | ||||
|     // We probably don't have a .git folder, which could happen in container builds. | ||||
| } | ||||
|  | ||||
| const packageJSONPath = path.join(authentikProjectRoot, "./package.json"); | ||||
| const rootPackage = JSON.parse(readFileSync(packageJSONPath, "utf8")); | ||||
|  | ||||
| const NODE_ENV = process.env.NODE_ENV || "development"; | ||||
| const AK_API_BASE_PATH = process.env.AK_API_BASE_PATH || ""; | ||||
|  | ||||
| const environmentVars = new Map([ | ||||
|     ["NODE_ENV", NODE_ENV], | ||||
|     ["CWD", cwd()], | ||||
|     ["AK_API_BASE_PATH", AK_API_BASE_PATH], | ||||
| ]); | ||||
|  | ||||
| const definitions = Object.fromEntries( | ||||
|     Array.from(environmentVars).map(([key, value]) => { | ||||
|         return [`process.env.${key}`, JSON.stringify(value)]; | ||||
|     }), | ||||
| ); | ||||
| const patternflyPath = resolvePackage("@patternfly/patternfly"); | ||||
|  | ||||
| /** | ||||
|  * All is magic is just to make sure the assets are copied into the right places. This is a very | ||||
|  * stripped down version of what the rollup-copy-plugin does, without any of the features we don't | ||||
|  * use, and using globSync instead of globby since we already had globSync lying around thanks to | ||||
|  * Typescript. If there's a third argument in an array entry, it's used to replace the internal path | ||||
|  * before concatenating it all together as the destination target. | ||||
|  * @type {Array<[string, string, string?]>} | ||||
|  */ | ||||
| const assetsFileMappings = [ | ||||
|     ["node_modules/@patternfly/patternfly/patternfly.min.css", "."], | ||||
|     ["node_modules/@patternfly/patternfly/assets/**", ".", "node_modules/@patternfly/patternfly/"], | ||||
|     ["src/common/styles/**", "."], | ||||
|     ["src/assets/images/**", "./assets/images"], | ||||
|     ["./icons/*", "./assets/icons"], | ||||
| ]; | ||||
|  | ||||
| /** | ||||
|  * @param {string} filePath | ||||
|  */ | ||||
| const isFile = (filePath) => statSync(filePath).isFile(); | ||||
|  | ||||
| /** | ||||
|  * @param {string} src Source file | ||||
|  * @param {string} dest Destination folder | ||||
|  * @param {string} [strip] Path to strip from the source file | ||||
|  */ | ||||
| function nameCopyTarget(src, dest, strip) { | ||||
|     const target = path.join(dest, strip ? src.replace(strip, "") : path.parse(src).base); | ||||
|     return [src, target]; | ||||
| } | ||||
|  | ||||
| for (const [source, rawdest, strip] of assetsFileMappings) { | ||||
|     const matchedPaths = globSync(source); | ||||
|     const dest = path.join("dist", rawdest); | ||||
|  | ||||
|     const copyTargets = matchedPaths.map((path) => nameCopyTarget(path, dest, strip)); | ||||
|  | ||||
|     for (const [src, dest] of copyTargets) { | ||||
|         if (isFile(src)) { | ||||
|             mkdirSync(path.dirname(dest), { recursive: true }); | ||||
|             copyFileSync(src, dest); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @typedef {[source: string, destination: string]} EntryPoint | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * This starts the definitions used for esbuild: Our targets, our arguments, the function for | ||||
|  * running a build, and three options for building: watching, building, and building the proxy. | ||||
|  * Ordered by largest to smallest interface to build even faster | ||||
|  * | ||||
|  * @type {EntryPoint[]} | ||||
|  */ | ||||
| const entryPoints = [ | ||||
|     ["admin/AdminInterface/AdminInterface.ts", "admin"], | ||||
|     ["user/UserInterface.ts", "user"], | ||||
|     ["flow/FlowInterface.ts", "flow"], | ||||
|     ["standalone/api-browser/index.ts", "standalone/api-browser"], | ||||
|     ["rac/index.ts", "rac"], | ||||
|     ["standalone/loading/index.ts", "standalone/loading"], | ||||
|     ["polyfill/poly.ts", "."], | ||||
| ]; | ||||
|  | ||||
| /** | ||||
|  * @type {import("esbuild").BuildOptions} | ||||
|  * @type {Readonly<BuildOptions>} | ||||
|  */ | ||||
| const BASE_ESBUILD_OPTIONS = { | ||||
|     entryNames: `[dir]/[name]-${readBuildIdentifier()}`, | ||||
|     chunkNames: "[dir]/chunks/[name]-[hash]", | ||||
|     assetNames: "assets/[dir]/[name]-[hash]", | ||||
|     publicPath: path.join("/static", DistDirectoryName), | ||||
|     outdir: DistDirectory, | ||||
|     bundle: true, | ||||
|     write: true, | ||||
|     sourcemap: true, | ||||
|     minify: NODE_ENV === "production", | ||||
|     minify: NodeEnvironment === "production", | ||||
|     legalComments: "external", | ||||
|     splitting: true, | ||||
|     treeShaking: true, | ||||
|     external: ["*.woff", "*.woff2"], | ||||
|     tsconfig: path.resolve(__dirname, "..", "tsconfig.build.json"), | ||||
|     tsconfig: path.resolve(PackageRoot, "tsconfig.build.json"), | ||||
|     loader: { | ||||
|         ".css": "text", | ||||
|     }, | ||||
|     plugins: [ | ||||
|         copy({ | ||||
|             assets: [ | ||||
|                 { | ||||
|                     from: path.join(patternflyPath, "patternfly.min.css"), | ||||
|                     to: ".", | ||||
|                 }, | ||||
|                 { | ||||
|                     from: path.join(patternflyPath, "assets", "**"), | ||||
|                     to: "./assets", | ||||
|                 }, | ||||
|                 { | ||||
|                     from: path.resolve(PackageRoot, "src", "common", "styles", "**"), | ||||
|                     to: ".", | ||||
|                 }, | ||||
|                 { | ||||
|                     from: path.resolve(PackageRoot, "src", "assets", "images", "**"), | ||||
|                     to: "./assets/images", | ||||
|                 }, | ||||
|                 { | ||||
|                     from: path.resolve(PackageRoot, "icons", "*"), | ||||
|                     to: "./assets/icons", | ||||
|                 }, | ||||
|             ], | ||||
|         }), | ||||
|         polyfillNode({ | ||||
|             polyfills: { | ||||
|                 path: true, | ||||
|             }, | ||||
|         }), | ||||
|         mdxPlugin({ | ||||
|             root: authentikProjectRoot, | ||||
|             root: MonoRepoRoot, | ||||
|         }), | ||||
|     ], | ||||
|     define: definitions, | ||||
|     define: createBundleDefinitions(), | ||||
|     format: "esm", | ||||
|     logOverride: { | ||||
|         /** | ||||
| @ -151,69 +94,43 @@ const BASE_ESBUILD_OPTIONS = { | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Creates a version ID for the build. | ||||
|  * @returns {string} | ||||
|  */ | ||||
| function composeVersionID() { | ||||
|     const { version } = rootPackage; | ||||
|     const buildHash = process.env.GIT_BUILD_HASH; | ||||
| async function cleanDistDirectory() { | ||||
|     const timerLabel = `${logPrefix} ♻️ Cleaning previous builds...`; | ||||
|  | ||||
|     if (buildHash) { | ||||
|         return `${version}+${buildHash}`; | ||||
|     } | ||||
|     console.time(timerLabel); | ||||
|  | ||||
|     return version; | ||||
|     await fs.rm(DistDirectory, { | ||||
|         recursive: true, | ||||
|         force: true, | ||||
|     }); | ||||
|  | ||||
|     await fs.mkdir(DistDirectory, { | ||||
|         recursive: true, | ||||
|     }); | ||||
|  | ||||
|     console.timeEnd(timerLabel); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Build a single entry point. | ||||
|  * Creates an ESBuild options, extending the base options with the given overrides. | ||||
|  * | ||||
|  * @param {EntryPoint} buildTarget | ||||
|  * @param {Partial<esbuild.BuildOptions>} [overrides] | ||||
|  * @throws {Error} on build failure | ||||
|  * @param {BuildOptions} overrides | ||||
|  * @returns {BuildOptions} | ||||
|  */ | ||||
| function createEntryPointOptions([source, dest], overrides = {}) { | ||||
|     const outdir = path.join(__dirname, "..", "dist", dest); | ||||
|  | ||||
| export function createESBuildOptions(overrides) { | ||||
|     /** | ||||
|      * @type {esbuild.BuildOptions} | ||||
|      * @type {BuildOptions} | ||||
|      */ | ||||
|     const mergedOptions = deepmerge(BASE_ESBUILD_OPTIONS, overrides); | ||||
|  | ||||
|     const entryPointConfig = { | ||||
|         entryPoints: [`./src/${source}`], | ||||
|         entryNames: `[dir]/[name]-${composeVersionID()}`, | ||||
|         publicPath: path.join("/static", "dist", dest), | ||||
|         outdir, | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * @type {esbuild.BuildOptions} | ||||
|      */ | ||||
|     const mergedConfig = deepmerge(BASE_ESBUILD_OPTIONS, entryPointConfig, overrides); | ||||
|  | ||||
|     return mergedConfig; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Build all entry points in parallel. | ||||
|  * | ||||
|  * @param {EntryPoint[]} entryPoints | ||||
|  * @returns {Promise<esbuild.BuildResult[]>} | ||||
|  */ | ||||
| async function buildParallel(entryPoints) { | ||||
|     return Promise.all( | ||||
|         entryPoints.map((entryPoint) => { | ||||
|             return esbuild.build(createEntryPointOptions(entryPoint)); | ||||
|         }), | ||||
|     ); | ||||
|     return mergedOptions; | ||||
| } | ||||
|  | ||||
| function doHelp() { | ||||
|     console.log(`Build the authentik UI | ||||
|  | ||||
|         options: | ||||
|             -w, --watch: Build all ${entryPoints.length} interfaces | ||||
|             -w, --watch: Build all interfaces | ||||
|             -p, --proxy: Build only the polyfills and the loading application | ||||
|             -h, --help: This help message | ||||
| `); | ||||
| @ -222,27 +139,29 @@ function doHelp() { | ||||
| } | ||||
|  | ||||
| async function doWatch() { | ||||
|     console.log("Watching all entry points..."); | ||||
|     console.group(`${logPrefix} 🤖 Watching entry points`); | ||||
|  | ||||
|     const buildContexts = await Promise.all( | ||||
|         entryPoints.map((entryPoint) => { | ||||
|             return esbuild.context( | ||||
|                 createEntryPointOptions(entryPoint, { | ||||
|                     define: definitions, | ||||
|                     plugins: [ | ||||
|                         liveReloadPlugin({ | ||||
|                             logPrefix: `Build Observer (${entryPoint[1]})`, | ||||
|                             relativeRoot: path.join(__dirname, ".."), | ||||
|                         }), | ||||
|                     ], | ||||
|                 }), | ||||
|             ); | ||||
|         }), | ||||
|     ); | ||||
|     const entryPoints = Object.entries(EntryPoint).map(([entrypointID, target]) => { | ||||
|         console.log(entrypointID); | ||||
|  | ||||
|     await Promise.all(buildContexts.map((context) => context.rebuild())); | ||||
|         return target; | ||||
|     }); | ||||
|  | ||||
|     await Promise.allSettled(buildContexts.map((context) => context.watch())); | ||||
|     console.groupEnd(); | ||||
|  | ||||
|     const buildOptions = createESBuildOptions({ | ||||
|         entryPoints, | ||||
|         plugins: [ | ||||
|             liveReloadPlugin({ | ||||
|                 relativeRoot: PackageRoot, | ||||
|             }), | ||||
|         ], | ||||
|     }); | ||||
|  | ||||
|     const buildContext = await esbuild.context(buildOptions); | ||||
|  | ||||
|     await buildContext.rebuild(); | ||||
|     await buildContext.watch(); | ||||
|  | ||||
|     return /** @type {Promise<void>} */ ( | ||||
|         new Promise((resolve) => { | ||||
| @ -254,15 +173,34 @@ async function doWatch() { | ||||
| } | ||||
|  | ||||
| async function doBuild() { | ||||
|     console.log("Building all entry points"); | ||||
|     console.group(`${logPrefix} 🚀 Building entry points:`); | ||||
|  | ||||
|     return buildParallel(entryPoints); | ||||
|     const entryPoints = Object.entries(EntryPoint).map(([entrypointID, target]) => { | ||||
|         console.log(entrypointID); | ||||
|  | ||||
|         return target; | ||||
|     }); | ||||
|  | ||||
|     console.groupEnd(); | ||||
|  | ||||
|     const buildOptions = createESBuildOptions({ | ||||
|         entryPoints, | ||||
|     }); | ||||
|  | ||||
|     await esbuild.build(buildOptions); | ||||
|  | ||||
|     console.log("Build complete"); | ||||
| } | ||||
|  | ||||
| async function doProxy() { | ||||
|     return buildParallel( | ||||
|         entryPoints.filter(([_, dest]) => ["standalone/loading", "."].includes(dest)), | ||||
|     ); | ||||
|     const entryPoints = [EntryPoint.StandaloneLoading]; | ||||
|  | ||||
|     const buildOptions = createESBuildOptions({ | ||||
|         entryPoints, | ||||
|     }); | ||||
|  | ||||
|     await esbuild.build(buildOptions); | ||||
|     console.log("Proxy build complete"); | ||||
| } | ||||
|  | ||||
| async function delegateCommand() { | ||||
| @ -284,12 +222,16 @@ async function delegateCommand() { | ||||
|     } | ||||
| } | ||||
|  | ||||
| await delegateCommand() | ||||
|     .then(() => { | ||||
|         console.log("Build complete"); | ||||
|         process.exit(0); | ||||
|     }) | ||||
|     .catch((error) => { | ||||
|         console.error(error); | ||||
|         process.exit(1); | ||||
|     }); | ||||
| await cleanDistDirectory() | ||||
|     // --- | ||||
|     .then(() => | ||||
|         delegateCommand() | ||||
|             .then(() => { | ||||
|                 console.log("Build complete"); | ||||
|                 process.exit(0); | ||||
|             }) | ||||
|             .catch((error) => { | ||||
|                 console.error(error); | ||||
|                 process.exit(1); | ||||
|             }), | ||||
|     ); | ||||
|  | ||||
							
								
								
									
										29
									
								
								web/scripts/esbuild/environment.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								web/scripts/esbuild/environment.mjs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| /** | ||||
|  * @file ESBuild environment utilities. | ||||
|  */ | ||||
| import { AuthentikVersion, NodeEnvironment, serializeEnvironmentVars } from "@goauthentik/monorepo"; | ||||
|  | ||||
| /** | ||||
|  * Creates a mapping of environment variables to their respective runtime constants. | ||||
|  */ | ||||
| export function createBundleDefinitions() { | ||||
|     const SerializedNodeEnvironment = /** @type {`"development"` | `"production"`} */ ( | ||||
|         JSON.stringify(NodeEnvironment) | ||||
|     ); | ||||
|  | ||||
|     /** | ||||
|      * @satisfies {Record<ESBuildImportEnvKey, string>} | ||||
|      */ | ||||
|     const envRecord = { | ||||
|         AK_VERSION: AuthentikVersion, | ||||
|         AK_API_BASE_PATH: process.env.AK_API_BASE_PATH ?? "", | ||||
|     }; | ||||
|  | ||||
|     return { | ||||
|         ...serializeEnvironmentVars(envRecord), | ||||
|         // We need to explicitly set this for NPM packages that use `process` | ||||
|         // to determine their environment. | ||||
|         "process.env.NODE_ENV": SerializedNodeEnvironment, | ||||
|         "import.meta.env.NODE_ENV": SerializedNodeEnvironment, | ||||
|     }; | ||||
| } | ||||
| @ -35,6 +35,11 @@ const __dirname = fileURLToPath(new URL(".", import.meta.url)); | ||||
| const projectRoot = path.join(__dirname, ".."); | ||||
| process.chdir(projectRoot); | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @param {string[]} flags | ||||
|  * @returns | ||||
|  */ | ||||
| const hasFlag = (flags) => process.argv.length > 1 && flags.includes(process.argv[2]); | ||||
|  | ||||
| const [configFile, files] = hasFlag(["-n", "--nightmare"]) | ||||
|  | ||||
| @ -1,33 +0,0 @@ | ||||
| #!/usr/bin/env bash | ||||
|  | ||||
| TARGET="./node_modules/@spotlightjs/overlay/dist/index-"[0-9a-f]*.js | ||||
|  | ||||
| if [[ $(grep -L "QX2" "$TARGET" > /dev/null 2> /dev/null) ]]; then | ||||
|     patch --forward -V none --no-backup-if-mismatch -p0 $TARGET <<EOF | ||||
|  | ||||
| TARGET=$(find "./node_modules/@spotlightjs/overlay/dist/" -name "index-[0-9a-f]*.js"); | ||||
|  | ||||
| if ! grep -GL 'QX2 = ' "$TARGET" > /dev/null ; then | ||||
| patch --forward --no-backup-if-mismatch -p0 "$TARGET" <<EOF | ||||
| >>>>>>> main | ||||
| --- a/index-5682ce90.js	2024-06-13 16:19:28 | ||||
| +++ b/index-5682ce90.js	2024-06-13 16:20:23 | ||||
| @@ -4958,11 +4958,10 @@ | ||||
|      } | ||||
|    ); | ||||
|  } | ||||
| -const q2 = w.lazy(() => import("./main-3257b7fc.js").then((n) => n.m)); | ||||
| +const q2 = w.lazy(() => import("./main-3257b7fc.js").then((n) => n.m)), QX2 = () => {}; | ||||
|  function Gp({ | ||||
|    data: n, | ||||
| -  onUpdateData: a = () => { | ||||
| -  }, | ||||
| +  onUpdateData: a = QX2, | ||||
|    editingEnabled: s = !1, | ||||
|    clipboardEnabled: o = !1, | ||||
|    displayDataTypes: c = !1, | ||||
| EOF | ||||
|  | ||||
| else | ||||
|     echo "spotlight overlay.js patch already applied" | ||||
| fi | ||||
| @ -1,22 +1,36 @@ | ||||
| import { readFileSync } from "fs"; | ||||
| import path from "path"; | ||||
| /** | ||||
|  * @file Pseudo-localization script. | ||||
|  * | ||||
|  * @import { ConfigFile } from "@lit/localize-tools/lib/types/config.js" | ||||
|  * @import { Config } from '@lit/localize-tools/lib/types/config.js'; | ||||
|  * @import { ProgramMessage } from "@lit/localize-tools/src/messages.js" | ||||
|  * @import { Locale } from "@lit/localize-tools/src/types/locale.js" | ||||
|  */ | ||||
| import { PackageRoot } from "@goauthentik/web/paths"; | ||||
| import { readFileSync } from "node:fs"; | ||||
| import path from "node:path"; | ||||
| import pseudolocale from "pseudolocale"; | ||||
| import { fileURLToPath } from "url"; | ||||
|  | ||||
| import { makeFormatter } from "@lit/localize-tools/lib/formatters/index.js"; | ||||
| import { sortProgramMessages } from "@lit/localize-tools/lib/messages.js"; | ||||
| import { TransformLitLocalizer } from "@lit/localize-tools/lib/modes/transform.js"; | ||||
|  | ||||
| const __dirname = fileURLToPath(new URL(".", import.meta.url)); | ||||
| const pseudoLocale = "pseudo-LOCALE"; | ||||
| const pseudoLocale = /** @type {Locale} */ ("pseudo-LOCALE"); | ||||
| const targetLocales = [pseudoLocale]; | ||||
| const baseConfig = JSON.parse(readFileSync(path.join(__dirname, "../lit-localize.json"), "utf-8")); | ||||
|  | ||||
| /** | ||||
|  * @type {ConfigFile} | ||||
|  */ | ||||
| const baseConfig = JSON.parse(readFileSync(path.join(PackageRoot, "lit-localize.json"), "utf-8")); | ||||
|  | ||||
| // Need to make some internal specifications to satisfy the transformer. It doesn't actually matter | ||||
| // which Localizer we use (transformer or runtime), because all of the functionality we care about | ||||
| // is in their common parent class, but I had to pick one.  Everything else here is just pure | ||||
| // exploitation of the lit/localize-tools internals. | ||||
|  | ||||
| /** | ||||
|  * @satisfies {Config} | ||||
|  */ | ||||
| const config = { | ||||
|     ...baseConfig, | ||||
|     baseDir: path.join(__dirname, ".."), | ||||
| @ -28,6 +42,11 @@ const config = { | ||||
|     resolve: (path) => path, | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @param {ProgramMessage} message | ||||
|  * @returns | ||||
|  */ | ||||
| const pseudoMessagify = (message) => ({ | ||||
|     name: message.name, | ||||
|     contents: message.contents.map((content) => | ||||
| @ -36,7 +55,7 @@ const pseudoMessagify = (message) => ({ | ||||
| }); | ||||
|  | ||||
| const localizer = new TransformLitLocalizer(config); | ||||
| const messages = localizer.extractSourceMessages().messages; | ||||
| const { messages } = localizer.extractSourceMessages(); | ||||
| const translations = messages.map(pseudoMessagify); | ||||
| const sorted = sortProgramMessages([...messages]); | ||||
| const formatter = makeFormatter(config); | ||||
|  | ||||
| @ -1,11 +1,11 @@ | ||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||
| import { VERSION } from "@goauthentik/common/constants"; | ||||
| import { globalAK } from "@goauthentik/common/global"; | ||||
| import { DefaultBrand } from "@goauthentik/common/ui/config"; | ||||
| import "@goauthentik/elements/EmptyState"; | ||||
| import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider"; | ||||
| import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider"; | ||||
| import { ModalButton } from "@goauthentik/elements/buttons/ModalButton"; | ||||
| import { DefaultBrand } from "@goauthentik/elements/sidebar/SidebarBrand"; | ||||
|  | ||||
| import { msg } from "@lit/localize"; | ||||
| import { TemplateResult, css, html } from "lit"; | ||||
|  | ||||
| @ -1,186 +1,97 @@ | ||||
| import { EVENT_SIDEBAR_TOGGLE } from "@goauthentik/common/constants"; | ||||
| import { me } from "@goauthentik/common/users"; | ||||
| import { AKElement } from "@goauthentik/elements/Base"; | ||||
| import { | ||||
|     CapabilitiesEnum, | ||||
|     WithCapabilitiesConfig, | ||||
| } from "@goauthentik/elements/Interface/capabilitiesProvider"; | ||||
| import { WithVersion } from "@goauthentik/elements/Interface/versionProvider"; | ||||
| import { ID_REGEX, SLUG_REGEX, UUID_REGEX } from "@goauthentik/elements/router/Route"; | ||||
| import { getRootStyle } from "@goauthentik/elements/utils/getRootStyle"; | ||||
| import { spread } from "@open-wc/lit-helpers"; | ||||
|  | ||||
| import { msg } from "@lit/localize"; | ||||
| import { TemplateResult, html, nothing } from "lit"; | ||||
| import { customElement, property, state } from "lit/decorators.js"; | ||||
| import { map } from "lit/directives/map.js"; | ||||
| import { repeat } from "lit/directives/repeat.js"; | ||||
|  | ||||
| import { UiThemeEnum } from "@goauthentik/api"; | ||||
| import type { SessionUser, UserSelf } from "@goauthentik/api"; | ||||
| // The second attribute type is of string[] to help with the 'activeWhen' control, which was | ||||
| // commonplace and singular enough to merit its own handler. | ||||
| type SidebarEntry = [ | ||||
|     path: string | null, | ||||
|     label: string, | ||||
|     attributes?: Record<string, any> | string[] | null, // eslint-disable-line | ||||
|     children?: SidebarEntry[], | ||||
| ]; | ||||
|  | ||||
| @customElement("ak-admin-sidebar") | ||||
| export class AkAdminSidebar extends WithCapabilitiesConfig(WithVersion(AKElement)) { | ||||
|     @property({ type: Boolean, reflect: true }) | ||||
|     open = true; | ||||
| /** | ||||
|  * Recursively renders a sidebar entry. | ||||
|  */ | ||||
| export function renderSidebarItem([ | ||||
|     path, | ||||
|     label, | ||||
|     attributes, | ||||
|     children, | ||||
| ]: SidebarEntry): TemplateResult { | ||||
|     const properties = Array.isArray(attributes) | ||||
|         ? { ".activeWhen": attributes } | ||||
|         : (attributes ?? {}); | ||||
|  | ||||
|     @state() | ||||
|     impersonation: UserSelf["username"] | null = null; | ||||
|  | ||||
|     constructor() { | ||||
|         super(); | ||||
|         me().then((user: SessionUser) => { | ||||
|             this.impersonation = user.original ? user.user.username : null; | ||||
|         }); | ||||
|         this.toggleOpen = this.toggleOpen.bind(this); | ||||
|         this.checkWidth = this.checkWidth.bind(this); | ||||
|     if (path) { | ||||
|         properties.path = path; | ||||
|     } | ||||
|  | ||||
|     // This has to be a bound method so the event listener can be removed on disconnection as | ||||
|     // needed. | ||||
|     toggleOpen() { | ||||
|         this.open = !this.open; | ||||
|     } | ||||
|  | ||||
|     checkWidth() { | ||||
|         // This works just fine, but it assumes that the `--ak-sidebar--minimum-auto-width` is in | ||||
|         // REMs. If that changes, this code will have to be adjusted as well. | ||||
|         const minWidth = | ||||
|             parseFloat(getRootStyle("--ak-sidebar--minimum-auto-width")) * | ||||
|             parseFloat(getRootStyle("font-size")); | ||||
|         this.open = window.innerWidth >= minWidth; | ||||
|     } | ||||
|  | ||||
|     connectedCallback() { | ||||
|         super.connectedCallback(); | ||||
|         window.addEventListener(EVENT_SIDEBAR_TOGGLE, this.toggleOpen); | ||||
|         window.addEventListener("resize", this.checkWidth); | ||||
|         // After connecting to the DOM, we can now perform this check to see if the sidebar should | ||||
|         // be open by default. | ||||
|         this.checkWidth(); | ||||
|     } | ||||
|  | ||||
|     // The symmetry (☟, ☝) here is critical in that you want to start adding these handlers after | ||||
|     // connection, and removing them before disconnection. | ||||
|  | ||||
|     disconnectedCallback() { | ||||
|         window.removeEventListener(EVENT_SIDEBAR_TOGGLE, this.toggleOpen); | ||||
|         window.removeEventListener("resize", this.checkWidth); | ||||
|         super.disconnectedCallback(); | ||||
|     } | ||||
|  | ||||
|     render() { | ||||
|         return html` | ||||
|             <ak-sidebar | ||||
|                 class="pf-c-page__sidebar ${this.open ? "pf-m-expanded" : "pf-m-collapsed"} ${this | ||||
|                     .activeTheme === UiThemeEnum.Light | ||||
|                     ? "pf-m-light" | ||||
|                     : ""}" | ||||
|             > | ||||
|                 ${this.renderSidebarItems()} | ||||
|             </ak-sidebar> | ||||
|         `; | ||||
|     } | ||||
|  | ||||
|     updated() { | ||||
|         // This is permissible as`:host.classList` is not one of the properties Lit uses as a | ||||
|         // scheduling trigger. This sort of shenanigans can trigger an loop, in that it will trigger | ||||
|         // a browser reflow, which may trigger some other styling the application is monitoring, | ||||
|         // triggering a re-render which triggers a browser reflow, ad infinitum. But we've been | ||||
|         // living with that since jQuery, and it's both well-known and fortunately rare. | ||||
|  | ||||
|         // eslint-disable-next-line wc/no-self-class | ||||
|         this.classList.remove("pf-m-expanded", "pf-m-collapsed"); | ||||
|         // eslint-disable-next-line wc/no-self-class | ||||
|         this.classList.add(this.open ? "pf-m-expanded" : "pf-m-collapsed"); | ||||
|     } | ||||
|  | ||||
|     renderSidebarItems(): TemplateResult { | ||||
|         // The second attribute type is of string[] to help with the 'activeWhen' control, which was | ||||
|         // commonplace and singular enough to merit its own handler. | ||||
|         type SidebarEntry = [ | ||||
|             path: string | null, | ||||
|             label: string, | ||||
|             attributes?: Record<string, any> | string[] | null, // eslint-disable-line | ||||
|             children?: SidebarEntry[], | ||||
|         ]; | ||||
|  | ||||
|         // prettier-ignore | ||||
|         const sidebarContent: SidebarEntry[] = [ | ||||
|             [null, msg("Dashboards"), { "?expanded": true }, [ | ||||
|                 ["/administration/overview", msg("Overview")], | ||||
|                 ["/administration/dashboard/users", msg("User Statistics")], | ||||
|                 ["/administration/system-tasks", msg("System Tasks")]]], | ||||
|             [null, msg("Applications"), null, [ | ||||
|                 ["/core/applications", msg("Applications"), [`^/core/applications/(?<slug>${SLUG_REGEX})$`]], | ||||
|                 ["/core/providers", msg("Providers"), [`^/core/providers/(?<id>${ID_REGEX})$`]], | ||||
|                 ["/outpost/outposts", msg("Outposts")]]], | ||||
|             [null, msg("Events"), null, [ | ||||
|                 ["/events/log", msg("Logs"), [`^/events/log/(?<id>${UUID_REGEX})$`]], | ||||
|                 ["/events/rules", msg("Notification Rules")], | ||||
|                 ["/events/transports", msg("Notification Transports")]]], | ||||
|             [null, msg("Customization"), null, [ | ||||
|                 ["/policy/policies", msg("Policies")], | ||||
|                 ["/core/property-mappings", msg("Property Mappings")], | ||||
|                 ["/blueprints/instances", msg("Blueprints")], | ||||
|                 ["/policy/reputation", msg("Reputation scores")]]], | ||||
|             [null, msg("Flows and Stages"), null, [ | ||||
|                 ["/flow/flows", msg("Flows"), [`^/flow/flows/(?<slug>${SLUG_REGEX})$`]], | ||||
|                 ["/flow/stages", msg("Stages")], | ||||
|                 ["/flow/stages/prompts", msg("Prompts")]]], | ||||
|             [null, msg("Directory"), null, [ | ||||
|                 ["/identity/users", msg("Users"), [`^/identity/users/(?<id>${ID_REGEX})$`]], | ||||
|                 ["/identity/groups", msg("Groups"), [`^/identity/groups/(?<id>${UUID_REGEX})$`]], | ||||
|                 ["/identity/roles", msg("Roles"), [`^/identity/roles/(?<id>${UUID_REGEX})$`]], | ||||
|                 ["/identity/initial-permissions", msg("Initial Permissions"), [`^/identity/initial-permissions/(?<id>${ID_REGEX})$`]], | ||||
|                 ["/core/sources", msg("Federation and Social login"), [`^/core/sources/(?<slug>${SLUG_REGEX})$`]], | ||||
|                 ["/core/tokens", msg("Tokens and App passwords")], | ||||
|                 ["/flow/stages/invitations", msg("Invitations")]]], | ||||
|             [null, msg("System"), null, [ | ||||
|                 ["/core/brands", msg("Brands")], | ||||
|                 ["/crypto/certificates", msg("Certificates")], | ||||
|                 ["/outpost/integrations", msg("Outpost Integrations")], | ||||
|                 ["/admin/settings", msg("Settings")]]], | ||||
|         ]; | ||||
|  | ||||
|         // Typescript requires the type here to correctly type the recursive path | ||||
|         type SidebarRenderer = (_: SidebarEntry) => TemplateResult; | ||||
|  | ||||
|         const renderOneSidebarItem: SidebarRenderer = ([path, label, attributes, children]) => { | ||||
|             const properties = Array.isArray(attributes) | ||||
|                 ? { ".activeWhen": attributes } | ||||
|                 : (attributes ?? {}); | ||||
|             if (path) { | ||||
|                 properties.path = path; | ||||
|             } | ||||
|             return html`<ak-sidebar-item ${spread(properties)}> | ||||
|                 ${label ? html`<span slot="label">${label}</span>` : nothing} | ||||
|                 ${map(children, renderOneSidebarItem)} | ||||
|             </ak-sidebar-item>`; | ||||
|         }; | ||||
|  | ||||
|         // prettier-ignore | ||||
|         return html` | ||||
|             ${map(sidebarContent, renderOneSidebarItem)} | ||||
|             ${this.renderEnterpriseMenu()} | ||||
|         `; | ||||
|     } | ||||
|  | ||||
|     renderEnterpriseMenu() { | ||||
|         return this.can(CapabilitiesEnum.IsEnterprise) | ||||
|             ? html` | ||||
|                   <ak-sidebar-item> | ||||
|                       <span slot="label">${msg("Enterprise")}</span> | ||||
|                       <ak-sidebar-item path="/enterprise/licenses"> | ||||
|                           <span slot="label">${msg("Licenses")}</span> | ||||
|                       </ak-sidebar-item> | ||||
|                   </ak-sidebar-item> | ||||
|               ` | ||||
|             : nothing; | ||||
|     } | ||||
|     return html`<ak-sidebar-item ${spread(properties)}> | ||||
|         ${label ? html`<span slot="label">${label}</span>` : nothing} | ||||
|         ${children ? renderSidebarItems(children) : nothing} | ||||
|     </ak-sidebar-item>`; | ||||
| } | ||||
|  | ||||
| declare global { | ||||
|     interface HTMLElementTagNameMap { | ||||
|         "ak-admin-sidebar": AkAdminSidebar; | ||||
|     } | ||||
| /** | ||||
|  * Recursively renders a collection of sidebar entries. | ||||
|  */ | ||||
| export function renderSidebarItems(entries: readonly SidebarEntry[]) { | ||||
|     return repeat(entries, ([path, label]) => path || label, renderSidebarItem); | ||||
| } | ||||
|  | ||||
| // prettier-ignore | ||||
| export const AdminSidebarEntries: readonly SidebarEntry[] = [ | ||||
|     [null, msg("Dashboards"), { "?expanded": true }, [ | ||||
|         ["/administration/overview", msg("Overview")], | ||||
|         ["/administration/dashboard/users", msg("User Statistics")], | ||||
|         ["/administration/system-tasks", msg("System Tasks")]] | ||||
|     ], | ||||
|     [null, msg("Applications"), null, [ | ||||
|         ["/core/applications", msg("Applications"), [`^/core/applications/(?<slug>${SLUG_REGEX})$`]], | ||||
|         ["/core/providers", msg("Providers"), [`^/core/providers/(?<id>${ID_REGEX})$`]], | ||||
|         ["/outpost/outposts", msg("Outposts")]] | ||||
|     ], | ||||
|     [null, msg("Events"), null, [ | ||||
|         ["/events/log", msg("Logs"), [`^/events/log/(?<id>${UUID_REGEX})$`]], | ||||
|         ["/events/rules", msg("Notification Rules")], | ||||
|         ["/events/transports", msg("Notification Transports")]] | ||||
|     ], | ||||
|     [null, msg("Customization"), null, [ | ||||
|         ["/policy/policies", msg("Policies")], | ||||
|         ["/core/property-mappings", msg("Property Mappings")], | ||||
|         ["/blueprints/instances", msg("Blueprints")], | ||||
|         ["/policy/reputation", msg("Reputation scores")]] | ||||
|     ], | ||||
|     [null, msg("Flows and Stages"), null, [ | ||||
|         ["/flow/flows", msg("Flows"), [`^/flow/flows/(?<slug>${SLUG_REGEX})$`]], | ||||
|         ["/flow/stages", msg("Stages")], | ||||
|         ["/flow/stages/prompts", msg("Prompts")]] | ||||
|     ], | ||||
|     [null, msg("Directory"), null, [ | ||||
|         ["/identity/users", msg("Users"), [`^/identity/users/(?<id>${ID_REGEX})$`]], | ||||
|         ["/identity/groups", msg("Groups"), [`^/identity/groups/(?<id>${UUID_REGEX})$`]], | ||||
|         ["/identity/roles", msg("Roles"), [`^/identity/roles/(?<id>${UUID_REGEX})$`]], | ||||
|         ["/identity/initial-permissions", msg("Initial Permissions"), [`^/identity/initial-permissions/(?<id>${ID_REGEX})$`]], | ||||
|         ["/core/sources", msg("Federation and Social login"), [`^/core/sources/(?<slug>${SLUG_REGEX})$`]], | ||||
|         ["/core/tokens", msg("Tokens and App passwords")], | ||||
|         ["/flow/stages/invitations", msg("Invitations")]] | ||||
|     ], | ||||
|     [null, msg("System"), null, [ | ||||
|         ["/core/brands", msg("Brands")], | ||||
|         ["/crypto/certificates", msg("Certificates")], | ||||
|         ["/outpost/integrations", msg("Outpost Integrations")], | ||||
|         ["/admin/settings", msg("Settings")]] | ||||
|     ], | ||||
| ]; | ||||
|  | ||||
| // prettier-ignore | ||||
| export const AdminSidebarEnterpriseEntries: readonly SidebarEntry[] = [ | ||||
|     [null, msg("Enterprise"), null, [ | ||||
|         ["/enterprise/licenses", msg("Licenses"), null] | ||||
|     ], | ||||
| ]] | ||||
|  | ||||
| @ -9,8 +9,12 @@ import { configureSentry } from "@goauthentik/common/sentry"; | ||||
| import { me } from "@goauthentik/common/users"; | ||||
| import { WebsocketClient } from "@goauthentik/common/ws"; | ||||
| import { AuthenticatedInterface } from "@goauthentik/elements/Interface"; | ||||
| import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider.js"; | ||||
| import { SidebarToggleEventDetail } from "@goauthentik/elements/PageHeader"; | ||||
| import "@goauthentik/elements/ak-locale-context"; | ||||
| import "@goauthentik/elements/banner/EnterpriseStatusBanner"; | ||||
| import "@goauthentik/elements/banner/EnterpriseStatusBanner"; | ||||
| import "@goauthentik/elements/banner/VersionBanner"; | ||||
| import "@goauthentik/elements/banner/VersionBanner"; | ||||
| import "@goauthentik/elements/messages/MessageContainer"; | ||||
| import "@goauthentik/elements/messages/MessageContainer"; | ||||
| @ -21,79 +25,124 @@ import "@goauthentik/elements/router/RouterOutlet"; | ||||
| import "@goauthentik/elements/sidebar/Sidebar"; | ||||
| import "@goauthentik/elements/sidebar/SidebarItem"; | ||||
| 
 | ||||
| import { CSSResult, TemplateResult, css, html } from "lit"; | ||||
| import { customElement, property, query, state } from "lit/decorators.js"; | ||||
| import { CSSResult, TemplateResult, css, html, nothing } from "lit"; | ||||
| import { customElement, eventOptions, property, query } from "lit/decorators.js"; | ||||
| import { classMap } from "lit/directives/class-map.js"; | ||||
| 
 | ||||
| import PFButton from "@patternfly/patternfly/components/Button/button.css"; | ||||
| import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css"; | ||||
| import PFNav from "@patternfly/patternfly/components/Nav/nav.css"; | ||||
| import PFPage from "@patternfly/patternfly/components/Page/page.css"; | ||||
| import PFBase from "@patternfly/patternfly/patternfly-base.css"; | ||||
| 
 | ||||
| import { SessionUser, UiThemeEnum } from "@goauthentik/api"; | ||||
| import { LicenseSummaryStatusEnum, SessionUser, UiThemeEnum } from "@goauthentik/api"; | ||||
| 
 | ||||
| import "./AdminSidebar"; | ||||
| import { | ||||
|     AdminSidebarEnterpriseEntries, | ||||
|     AdminSidebarEntries, | ||||
|     renderSidebarItems, | ||||
| } from "./AdminSidebar.js"; | ||||
| 
 | ||||
| if (process.env.NODE_ENV === "development") { | ||||
| if (import.meta.env.NODE_ENV === "development") { | ||||
|     await import("@goauthentik/esbuild-plugin-live-reload/client"); | ||||
| } | ||||
| 
 | ||||
| @customElement("ak-interface-admin") | ||||
| export class AdminInterface extends AuthenticatedInterface { | ||||
|     @property({ type: Boolean }) | ||||
|     notificationDrawerOpen = getURLParam("notificationDrawerOpen", false); | ||||
| export class AdminInterface extends WithLicenseSummary(AuthenticatedInterface) { | ||||
|     //#region Properties
 | ||||
| 
 | ||||
|     @property({ type: Boolean }) | ||||
|     apiDrawerOpen = getURLParam("apiDrawerOpen", false); | ||||
|     public notificationDrawerOpen = getURLParam("notificationDrawerOpen", false); | ||||
| 
 | ||||
|     ws: WebsocketClient; | ||||
|     @property({ type: Boolean }) | ||||
|     public apiDrawerOpen = getURLParam("apiDrawerOpen", false); | ||||
| 
 | ||||
|     @state() | ||||
|     user?: SessionUser; | ||||
|     protected readonly ws: WebsocketClient; | ||||
| 
 | ||||
|     @property({ | ||||
|         type: Object, | ||||
|         attribute: false, | ||||
|         reflect: false, | ||||
|     }) | ||||
|     public user?: SessionUser; | ||||
| 
 | ||||
|     @query("ak-about-modal") | ||||
|     aboutModal?: AboutModal; | ||||
|     public aboutModal?: AboutModal; | ||||
| 
 | ||||
|     static get styles(): CSSResult[] { | ||||
|         return [ | ||||
|             PFBase, | ||||
|             PFPage, | ||||
|             PFButton, | ||||
|             PFDrawer, | ||||
|             css` | ||||
|                 .pf-c-page__main, | ||||
|                 .pf-c-drawer__content, | ||||
|                 .pf-c-page__drawer { | ||||
|                     z-index: auto !important; | ||||
|                     background-color: transparent; | ||||
|                 } | ||||
|                 .display-none { | ||||
|                     display: none; | ||||
|                 } | ||||
|                 .pf-c-page { | ||||
|                     background-color: var(--pf-c-page--BackgroundColor) !important; | ||||
|                 } | ||||
|                 /* Global page background colour */ | ||||
|                 :host([theme="dark"]) .pf-c-page { | ||||
|                     --pf-c-page--BackgroundColor: var(--ak-dark-background); | ||||
|                 } | ||||
|                 ak-enterprise-status, | ||||
|                 ak-version-banner { | ||||
|                     grid-area: header; | ||||
|                 } | ||||
|                 ak-admin-sidebar { | ||||
|                     grid-area: nav; | ||||
|                 } | ||||
|                 .pf-c-drawer__panel { | ||||
|                     z-index: var(--pf-global--ZIndex--xl); | ||||
|                 } | ||||
|             `,
 | ||||
|         ]; | ||||
|     @property({ type: Boolean, reflect: true }) | ||||
|     public sidebarOpen: boolean; | ||||
| 
 | ||||
|     @eventOptions({ passive: true }) | ||||
|     protected sidebarListener(event: CustomEvent<SidebarToggleEventDetail>) { | ||||
|         this.sidebarOpen = !!event.detail.open; | ||||
|     } | ||||
| 
 | ||||
|     #sidebarMatcher: MediaQueryList; | ||||
|     #sidebarMediaQueryListener = (event: MediaQueryListEvent) => { | ||||
|         this.sidebarOpen = event.matches; | ||||
|     }; | ||||
| 
 | ||||
|     //#endregion
 | ||||
| 
 | ||||
|     //#region Styles
 | ||||
| 
 | ||||
|     static styles: CSSResult[] = [ | ||||
|         PFBase, | ||||
|         PFPage, | ||||
|         PFButton, | ||||
|         PFDrawer, | ||||
|         PFNav, | ||||
|         css` | ||||
|             .pf-c-page__main, | ||||
|             .pf-c-drawer__content, | ||||
|             .pf-c-page__drawer { | ||||
|                 z-index: auto !important; | ||||
|                 background-color: transparent; | ||||
|             } | ||||
| 
 | ||||
|             .display-none { | ||||
|                 display: none; | ||||
|             } | ||||
| 
 | ||||
|             .pf-c-page { | ||||
|                 background-color: var(--pf-c-page--BackgroundColor) !important; | ||||
|             } | ||||
| 
 | ||||
|             :host([theme="dark"]) { | ||||
|                 /* Global page background colour */ | ||||
|                 .pf-c-page { | ||||
|                     --pf-c-page--BackgroundColor: var(--ak-dark-background); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             ak-page-navbar { | ||||
|                 grid-area: header; | ||||
|             } | ||||
| 
 | ||||
|             .ak-sidebar { | ||||
|                 grid-area: nav; | ||||
|             } | ||||
| 
 | ||||
|             .pf-c-drawer__panel { | ||||
|                 z-index: var(--pf-global--ZIndex--xl); | ||||
|             } | ||||
|         `,
 | ||||
|     ]; | ||||
| 
 | ||||
|     //#endregion
 | ||||
| 
 | ||||
|     //#region Lifecycle
 | ||||
| 
 | ||||
|     constructor() { | ||||
|         configureSentry(true); | ||||
|         super(); | ||||
|         this.ws = new WebsocketClient(); | ||||
|         this.#sidebarMatcher = window.matchMedia("(min-width: 1200px)"); | ||||
|         this.sidebarOpen = this.#sidebarMatcher.matches; | ||||
|     } | ||||
| 
 | ||||
|     public connectedCallback() { | ||||
|         super.connectedCallback(); | ||||
| 
 | ||||
|         window.addEventListener(EVENT_NOTIFICATION_DRAWER_TOGGLE, () => { | ||||
|             this.notificationDrawerOpen = !this.notificationDrawerOpen; | ||||
| @ -108,16 +157,25 @@ export class AdminInterface extends AuthenticatedInterface { | ||||
|                 apiDrawerOpen: this.apiDrawerOpen, | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         this.#sidebarMatcher.addEventListener("change", this.#sidebarMediaQueryListener, { | ||||
|             passive: true, | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     public disconnectedCallback(): void { | ||||
|         super.disconnectedCallback(); | ||||
|         this.#sidebarMatcher.removeEventListener("change", this.#sidebarMediaQueryListener); | ||||
|     } | ||||
| 
 | ||||
|     async firstUpdated(): Promise<void> { | ||||
|         configureSentry(true); | ||||
|         this.user = await me(); | ||||
| 
 | ||||
|         const canAccessAdmin = | ||||
|             this.user.user.isSuperuser || | ||||
|             // TODO: somehow add `access_admin_interface` to the API schema
 | ||||
|             this.user.user.systemPermissions.includes("access_admin_interface"); | ||||
| 
 | ||||
|         if (!canAccessAdmin && this.user.user.pk > 0) { | ||||
|             window.location.assign("/if/user/"); | ||||
|         } | ||||
| @ -125,10 +183,14 @@ export class AdminInterface extends AuthenticatedInterface { | ||||
| 
 | ||||
|     render(): TemplateResult { | ||||
|         const sidebarClasses = { | ||||
|             "pf-c-page__sidebar": true, | ||||
|             "pf-m-light": this.activeTheme === UiThemeEnum.Light, | ||||
|             "pf-m-expanded": this.sidebarOpen, | ||||
|             "pf-m-collapsed": !this.sidebarOpen, | ||||
|         }; | ||||
| 
 | ||||
|         const drawerOpen = this.notificationDrawerOpen || this.apiDrawerOpen; | ||||
| 
 | ||||
|         const drawerClasses = { | ||||
|             "pf-m-expanded": drawerOpen, | ||||
|             "pf-m-collapsed": !drawerOpen, | ||||
| @ -136,11 +198,18 @@ export class AdminInterface extends AuthenticatedInterface { | ||||
| 
 | ||||
|         return html` <ak-locale-context>
 | ||||
|             <div class="pf-c-page"> | ||||
|                 <ak-enterprise-status interface="admin"></ak-enterprise-status> | ||||
|                 <ak-version-banner></ak-version-banner> | ||||
|                 <ak-admin-sidebar | ||||
|                     class="pf-c-page__sidebar ${classMap(sidebarClasses)}" | ||||
|                 ></ak-admin-sidebar> | ||||
|                 <ak-page-navbar ?open=${this.sidebarOpen} @sidebar-toggle=${this.sidebarListener}> | ||||
|                     <ak-version-banner></ak-version-banner> | ||||
|                     <ak-enterprise-status interface="admin"></ak-enterprise-status> | ||||
|                 </ak-page-navbar> | ||||
| 
 | ||||
|                 <ak-sidebar class="${classMap(sidebarClasses)}"> | ||||
|                     ${renderSidebarItems(AdminSidebarEntries)} | ||||
|                     ${this.licenseSummary?.status !== LicenseSummaryStatusEnum.Unlicensed | ||||
|                         ? renderSidebarItems(AdminSidebarEnterpriseEntries) | ||||
|                         : nothing} | ||||
|                 </ak-sidebar> | ||||
| 
 | ||||
|                 <div class="pf-c-page__drawer"> | ||||
|                     <div class="pf-c-drawer ${classMap(drawerClasses)}"> | ||||
|                         <div class="pf-c-drawer__main"> | ||||
| @ -1,5 +0,0 @@ | ||||
| import { AdminInterface } from "./AdminInterface"; | ||||
| import "./AdminInterface"; | ||||
|  | ||||
| export { AdminInterface }; | ||||
| export default AdminInterface; | ||||
| @ -94,10 +94,13 @@ export class AdminOverviewPage extends AdminOverviewBase { | ||||
|     } | ||||
|  | ||||
|     render(): TemplateResult { | ||||
|         const name = this.user?.user.name ?? this.user?.user.username; | ||||
|         const username = this.user?.user.name || this.user?.user.username; | ||||
|  | ||||
|         return html`<ak-page-header description=${msg("General system status")} ?hasIcon=${false}> | ||||
|                 <span slot="header"> ${msg(str`Welcome, ${name || ""}.`)} </span> | ||||
|         return html` <ak-page-header | ||||
|                 header=${msg(str`Welcome, ${username || ""}.`)} | ||||
|                 description=${msg("General system status")} | ||||
|                 ?hasIcon=${false} | ||||
|             > | ||||
|             </ak-page-header> | ||||
|             <section class="pf-c-page__main-section"> | ||||
|                 <div class="pf-l-grid pf-m-gutter"> | ||||
|  | ||||
| @ -1,5 +1,4 @@ | ||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||
| import { first } from "@goauthentik/common/utils"; | ||||
| import "@goauthentik/components/ak-number-input"; | ||||
| import "@goauthentik/components/ak-switch-input"; | ||||
| import "@goauthentik/components/ak-text-input"; | ||||
| @ -184,20 +183,14 @@ export class AdminSettingsForm extends Form<SettingsRequest> { | ||||
|                 label=${msg("Reputation: lower limit")} | ||||
|                 required | ||||
|                 name="reputationLowerLimit" | ||||
|                 value="${first( | ||||
|                     this._settings?.reputationLowerLimit, | ||||
|                     DEFAULT_REPUTATION_LOWER_LIMIT, | ||||
|                 )}" | ||||
|                 value="${this._settings?.reputationLowerLimit ?? DEFAULT_REPUTATION_LOWER_LIMIT}" | ||||
|                 help=${msg("Reputation cannot decrease lower than this value. Zero or negative.")} | ||||
|             ></ak-number-input> | ||||
|             <ak-number-input | ||||
|                 label=${msg("Reputation: upper limit")} | ||||
|                 required | ||||
|                 name="reputationUpperLimit" | ||||
|                 value="${first( | ||||
|                     this._settings?.reputationUpperLimit, | ||||
|                     DEFAULT_REPUTATION_UPPER_LIMIT, | ||||
|                 )}" | ||||
|                 value="${this._settings?.reputationUpperLimit ?? DEFAULT_REPUTATION_UPPER_LIMIT}" | ||||
|                 help=${msg("Reputation cannot increase higher than this value. Zero or positive.")} | ||||
|             ></ak-number-input> | ||||
|             <ak-form-element-horizontal label=${msg("Footer links")} name="footerLinks"> | ||||
| @ -257,7 +250,7 @@ export class AdminSettingsForm extends Form<SettingsRequest> { | ||||
|                 label=${msg("Default token length")} | ||||
|                 required | ||||
|                 name="defaultTokenLength" | ||||
|                 value="${first(this._settings?.defaultTokenLength, 60)}" | ||||
|                 value="${this._settings?.defaultTokenLength ?? 60}" | ||||
|                 help=${msg("Default length of generated tokens")} | ||||
|             ></ak-number-input> | ||||
|         `; | ||||
|  | ||||
| @ -83,13 +83,10 @@ export class AdminSettingsPage extends AKElement { | ||||
|     } | ||||
|  | ||||
|     render() { | ||||
|         if (!this.settings) { | ||||
|             return nothing; | ||||
|         } | ||||
|         if (!this.settings) return nothing; | ||||
|  | ||||
|         return html` | ||||
|             <ak-page-header icon="fa fa-cog" header="" description=""> | ||||
|                 <span slot="header"> ${msg("System settings")} </span> | ||||
|             </ak-page-header> | ||||
|             <ak-page-header icon="fa fa-cog" header="${msg("System settings")}"> </ak-page-header> | ||||
|             <section class="pf-c-page__main-section pf-m-no-padding-mobile pf-l-grid pf-m-gutter"> | ||||
|                 <div class="pf-c-card"> | ||||
|                     <div class="pf-c-card__body"> | ||||
|  | ||||
| @ -1,7 +1,6 @@ | ||||
| import "@goauthentik/admin/applications/ProviderSelectModal"; | ||||
| import { iconHelperText } from "@goauthentik/admin/helperText"; | ||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||
| import { first } from "@goauthentik/common/utils"; | ||||
| import "@goauthentik/components/ak-file-input"; | ||||
| import "@goauthentik/components/ak-radio-input"; | ||||
| import "@goauthentik/components/ak-switch-input"; | ||||
| @ -194,7 +193,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio | ||||
|                     ></ak-text-input> | ||||
|                     <ak-switch-input | ||||
|                         name="openInNewTab" | ||||
|                         ?checked=${first(this.instance?.openInNewTab, false)} | ||||
|                         ?checked=${this.instance?.openInNewTab ?? false} | ||||
|                         label=${msg("Open in new tab")} | ||||
|                         help=${msg( | ||||
|                             "If checked, the launch URL will open in a new browser tab or window from the user's application library.", | ||||
| @ -221,7 +220,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio | ||||
|                         : html` <ak-text-input | ||||
|                               label=${msg("Icon")} | ||||
|                               name="metaIcon" | ||||
|                               value=${first(this.instance?.metaIcon, "")} | ||||
|                               value=${this.instance?.metaIcon ?? ""} | ||||
|                               help=${iconHelperText} | ||||
|                           > | ||||
|                           </ak-text-input>`} | ||||
|  | ||||
| @ -113,8 +113,7 @@ export class ApplicationViewPage extends AKElement { | ||||
|  | ||||
|     renderApp(): TemplateResult { | ||||
|         if (!this.application) { | ||||
|             return html`<ak-empty-state ?loading="${true}" header=${msg("Loading")}> | ||||
|             </ak-empty-state>`; | ||||
|             return html`<ak-empty-state loading header=${msg("Loading")}> </ak-empty-state>`; | ||||
|         } | ||||
|         return html`<ak-tabs> | ||||
|             ${this.missingOutpost | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	