Merge branch 'main' into dev
* main: web: clean up some repetitive types (#9241) core: fix logic for token expiration (#9426) ci: fix ci pipeline (#9427) translate: Updates for file locale/en/LC_MESSAGES/django.po in ru (#9424) web: Add resolved and integrity fields back to package-lock.json (#9419) translate: Updates for file locale/en/LC_MESSAGES/django.po in ru (#9407) stages/identification: don't check source component (#9410) core: bump selenium from 4.19.0 to 4.20.0 (#9411) core: bump black from 24.4.0 to 24.4.1 (#9412) ci: bump golangci/golangci-lint-action from 4 to 5 (#9413) core: bump goauthentik.io/api/v3 from 3.2024023.2 to 3.2024040.1 (#9414) web: bump @sentry/browser from 7.112.1 to 7.112.2 in /web in the sentry group (#9416) sources/oauth: ensure all UI sources return a valid source (#9401) web: markdown: display markdown even when frontmatter is missing (#9404)
This commit is contained in:
		
							
								
								
									
										2
									
								
								.github/workflows/ci-outpost.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci-outpost.yml
									
									
									
									
										vendored
									
									
								
							| @ -29,7 +29,7 @@ jobs: | |||||||
|       - name: Generate API |       - name: Generate API | ||||||
|         run: make gen-client-go |         run: make gen-client-go | ||||||
|       - name: golangci-lint |       - name: golangci-lint | ||||||
|         uses: golangci/golangci-lint-action@v4 |         uses: golangci/golangci-lint-action@v5 | ||||||
|         with: |         with: | ||||||
|           version: v1.54.2 |           version: v1.54.2 | ||||||
|           args: --timeout 5000s --verbose |           args: --timeout 5000s --verbose | ||||||
|  | |||||||
							
								
								
									
										8
									
								
								.github/workflows/ci-web.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/ci-web.yml
									
									
									
									
										vendored
									
									
								
							| @ -34,6 +34,13 @@ jobs: | |||||||
|       - name: Eslint |       - name: Eslint | ||||||
|         working-directory: ${{ matrix.project }}/ |         working-directory: ${{ matrix.project }}/ | ||||||
|         run: npm run lint |         run: npm run lint | ||||||
|  |   lint-lockfile: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/checkout@v4 | ||||||
|  |       - working-directory: web/ | ||||||
|  |         run: | | ||||||
|  |           [ -z "$(jq -r '.packages | to_entries[] | select((.key | startswith("node_modules")) and (.value | has("resolved") | not)) | .key' < package-lock.json)" ] | ||||||
|   lint-build: |   lint-build: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
| @ -95,6 +102,7 @@ jobs: | |||||||
|         run: npm run lit-analyse |         run: npm run lit-analyse | ||||||
|   ci-web-mark: |   ci-web-mark: | ||||||
|     needs: |     needs: | ||||||
|  |       - lint-lockfile | ||||||
|       - lint-eslint |       - lint-eslint | ||||||
|       - lint-prettier |       - lint-prettier | ||||||
|       - lint-lit-analyse |       - lint-lit-analyse | ||||||
|  | |||||||
							
								
								
									
										8
									
								
								.github/workflows/ci-website.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/ci-website.yml
									
									
									
									
										vendored
									
									
								
							| @ -12,6 +12,13 @@ on: | |||||||
|       - version-* |       - version-* | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|  |   lint-lockfile: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/checkout@v4 | ||||||
|  |       - working-directory: website/ | ||||||
|  |         run: | | ||||||
|  |           [ -z "$(jq -r '.packages | to_entries[] | select((.key | startswith("node_modules")) and (.value | has("resolved") | not)) | .key' < package-lock.json)" ] | ||||||
|   lint-prettier: |   lint-prettier: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
| @ -62,6 +69,7 @@ jobs: | |||||||
|         run: npm run ${{ matrix.job }} |         run: npm run ${{ matrix.job }} | ||||||
|   ci-website-mark: |   ci-website-mark: | ||||||
|     needs: |     needs: | ||||||
|  |       - lint-lockfile | ||||||
|       - lint-prettier |       - lint-prettier | ||||||
|       - test |       - test | ||||||
|       - build |       - build | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
|  |  | ||||||
| from typing import Any | from typing import Any | ||||||
|  |  | ||||||
|  | from django.utils.timezone import now | ||||||
| from django_filters.rest_framework import DjangoFilterBackend | from django_filters.rest_framework import DjangoFilterBackend | ||||||
| from drf_spectacular.utils import OpenApiResponse, extend_schema, inline_serializer | from drf_spectacular.utils import OpenApiResponse, extend_schema, inline_serializer | ||||||
| from guardian.shortcuts import assign_perm, get_anonymous_user | from guardian.shortcuts import assign_perm, get_anonymous_user | ||||||
| @ -27,7 +28,6 @@ from authentik.core.models import ( | |||||||
|     TokenIntents, |     TokenIntents, | ||||||
|     User, |     User, | ||||||
|     default_token_duration, |     default_token_duration, | ||||||
|     token_expires_from_timedelta, |  | ||||||
| ) | ) | ||||||
| from authentik.events.models import Event, EventAction | from authentik.events.models import Event, EventAction | ||||||
| from authentik.events.utils import model_to_dict | from authentik.events.utils import model_to_dict | ||||||
| @ -68,15 +68,17 @@ class TokenSerializer(ManagedSerializer, ModelSerializer): | |||||||
|             max_token_lifetime_dt = default_token_duration() |             max_token_lifetime_dt = default_token_duration() | ||||||
|             if max_token_lifetime is not None: |             if max_token_lifetime is not None: | ||||||
|                 try: |                 try: | ||||||
|                     max_token_lifetime_dt = timedelta_from_string(max_token_lifetime) |                     max_token_lifetime_dt = now() + timedelta_from_string(max_token_lifetime) | ||||||
|                 except ValueError: |                 except ValueError: | ||||||
|                     max_token_lifetime_dt = default_token_duration() |                     pass | ||||||
|  |  | ||||||
|             if "expires" in attrs and attrs.get("expires") > token_expires_from_timedelta( |             if "expires" in attrs and attrs.get("expires") > max_token_lifetime_dt: | ||||||
|                 max_token_lifetime_dt |  | ||||||
|             ): |  | ||||||
|                 raise ValidationError( |                 raise ValidationError( | ||||||
|                     {"expires": f"Token expires exceeds maximum lifetime ({max_token_lifetime})."} |                     { | ||||||
|  |                         "expires": ( | ||||||
|  |                             f"Token expires exceeds maximum lifetime ({max_token_lifetime_dt} UTC)." | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|                 ) |                 ) | ||||||
|         elif attrs.get("intent") == TokenIntents.INTENT_API: |         elif attrs.get("intent") == TokenIntents.INTENT_API: | ||||||
|             # For API tokens, expires cannot be overridden |             # For API tokens, expires cannot be overridden | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| """authentik core models""" | """authentik core models""" | ||||||
|  |  | ||||||
| from datetime import datetime, timedelta | from datetime import datetime | ||||||
| from hashlib import sha256 | from hashlib import sha256 | ||||||
| from typing import Any, Optional, Self | from typing import Any, Optional, Self | ||||||
| from uuid import uuid4 | from uuid import uuid4 | ||||||
| @ -68,11 +68,6 @@ def default_token_duration() -> datetime: | |||||||
|     return now() + timedelta_from_string(token_duration) |     return now() + timedelta_from_string(token_duration) | ||||||
|  |  | ||||||
|  |  | ||||||
| def token_expires_from_timedelta(dt: timedelta) -> datetime: |  | ||||||
|     """Return a `datetime.datetime` object with the duration of the Token""" |  | ||||||
|     return now() + dt |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def default_token_key() -> str: | def default_token_key() -> str: | ||||||
|     """Default token key""" |     """Default token key""" | ||||||
|     current_tenant = get_current_tenant() |     current_tenant = get_current_tenant() | ||||||
|  | |||||||
							
								
								
									
										37
									
								
								authentik/sources/oauth/tests/test_type_apple.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								authentik/sources/oauth/tests/test_type_apple.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | """Apple Type tests""" | ||||||
|  |  | ||||||
|  | from django.test import RequestFactory, TestCase | ||||||
|  | from guardian.shortcuts import get_anonymous_user | ||||||
|  |  | ||||||
|  | from authentik.lib.generators import generate_id | ||||||
|  | from authentik.lib.tests.utils import dummy_get_response | ||||||
|  | from authentik.root.middleware import SessionMiddleware | ||||||
|  | from authentik.sources.oauth.models import OAuthSource | ||||||
|  | from authentik.sources.oauth.types.registry import registry | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestTypeApple(TestCase): | ||||||
|  |     """OAuth Source tests""" | ||||||
|  |  | ||||||
|  |     def setUp(self): | ||||||
|  |         self.source = OAuthSource.objects.create( | ||||||
|  |             name="test", | ||||||
|  |             slug="test", | ||||||
|  |             provider_type="apple", | ||||||
|  |             authorization_url="", | ||||||
|  |             profile_url="", | ||||||
|  |             consumer_key=generate_id(), | ||||||
|  |         ) | ||||||
|  |         self.factory = RequestFactory() | ||||||
|  |  | ||||||
|  |     def test_login_challenge(self): | ||||||
|  |         """Test login_challenge""" | ||||||
|  |         request = self.factory.get("/") | ||||||
|  |         request.user = get_anonymous_user() | ||||||
|  |  | ||||||
|  |         middleware = SessionMiddleware(dummy_get_response) | ||||||
|  |         middleware.process_request(request) | ||||||
|  |         request.session.save() | ||||||
|  |         oauth_type = registry.find_type("apple") | ||||||
|  |         challenge = oauth_type().login_challenge(self.source, request) | ||||||
|  |         self.assertTrue(challenge.is_valid(raise_exception=True)) | ||||||
| @ -125,7 +125,7 @@ class AppleType(SourceType): | |||||||
|         ) |         ) | ||||||
|         args = apple_client.get_redirect_args() |         args = apple_client.get_redirect_args() | ||||||
|         return AppleLoginChallenge( |         return AppleLoginChallenge( | ||||||
|             instance={ |             data={ | ||||||
|                 "client_id": apple_client.get_client_id(), |                 "client_id": apple_client.get_client_id(), | ||||||
|                 "scope": "name email", |                 "scope": "name email", | ||||||
|                 "redirect_uri": args["redirect_uri"], |                 "redirect_uri": args["redirect_uri"], | ||||||
|  | |||||||
| @ -66,7 +66,7 @@ class PlexSource(Source): | |||||||
|             icon = static("authentik/sources/plex.svg") |             icon = static("authentik/sources/plex.svg") | ||||||
|         return UILoginButton( |         return UILoginButton( | ||||||
|             challenge=PlexAuthenticationChallenge( |             challenge=PlexAuthenticationChallenge( | ||||||
|                 { |                 data={ | ||||||
|                     "type": ChallengeTypes.NATIVE.value, |                     "type": ChallengeTypes.NATIVE.value, | ||||||
|                     "component": "ak-source-plex", |                     "component": "ak-source-plex", | ||||||
|                     "client_id": self.client_id, |                     "client_id": self.client_id, | ||||||
|  | |||||||
| @ -40,6 +40,11 @@ class TestPlexSource(TestCase): | |||||||
|             slug="test", |             slug="test", | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |     def test_login_challenge(self): | ||||||
|  |         """Test login_challenge""" | ||||||
|  |         ui_login_button = self.source.ui_login_button(None) | ||||||
|  |         self.assertTrue(ui_login_button.challenge.is_valid(raise_exception=True)) | ||||||
|  |  | ||||||
|     def test_get_user_info(self): |     def test_get_user_info(self): | ||||||
|         """Test get_user_info""" |         """Test get_user_info""" | ||||||
|         token = generate_key() |         token = generate_key() | ||||||
|  | |||||||
| @ -23,7 +23,7 @@ LOGGER = get_logger() | |||||||
|  |  | ||||||
| VALID_SCHEMA_NAME = re.compile(r"^t_[a-z0-9]{1,61}$") | VALID_SCHEMA_NAME = re.compile(r"^t_[a-z0-9]{1,61}$") | ||||||
|  |  | ||||||
| DEFAULT_TOKEN_DURATION = "minutes=30"  # nosec | DEFAULT_TOKEN_DURATION = "days=1"  # nosec | ||||||
| DEFAULT_TOKEN_LENGTH = 60 | DEFAULT_TOKEN_LENGTH = 60 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @ -28,7 +28,7 @@ require ( | |||||||
| 	github.com/spf13/cobra v1.8.0 | 	github.com/spf13/cobra v1.8.0 | ||||||
| 	github.com/stretchr/testify v1.9.0 | 	github.com/stretchr/testify v1.9.0 | ||||||
| 	github.com/wwt/guac v1.3.2 | 	github.com/wwt/guac v1.3.2 | ||||||
| 	goauthentik.io/api/v3 v3.2024023.2 | 	goauthentik.io/api/v3 v3.2024040.1 | ||||||
| 	golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab | 	golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab | ||||||
| 	golang.org/x/oauth2 v0.19.0 | 	golang.org/x/oauth2 v0.19.0 | ||||||
| 	golang.org/x/sync v0.7.0 | 	golang.org/x/sync v0.7.0 | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							| @ -294,8 +294,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.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= | ||||||
| go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= | go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= | ||||||
| go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= | go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= | ||||||
| goauthentik.io/api/v3 v3.2024023.2 h1:lSVaZAKTpsDhtw11wnkGjPalkDzv9H2VKEJllBi2aXs= | goauthentik.io/api/v3 v3.2024040.1 h1:0Mp8XLYuscQEWVTR2lNk74WLKDpOVHX0mlbvbvcC6fw= | ||||||
| goauthentik.io/api/v3 v3.2024023.2/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw= | goauthentik.io/api/v3 v3.2024040.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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||||
| golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||||
| golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||||
|  | |||||||
							
								
								
									
										3205
									
								
								locale/ru/LC_MESSAGES/django.po
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3205
									
								
								locale/ru/LC_MESSAGES/django.po
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										52
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										52
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							| @ -392,33 +392,33 @@ files = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "black" | name = "black" | ||||||
| version = "24.4.0" | version = "24.4.1" | ||||||
| description = "The uncompromising code formatter." | description = "The uncompromising code formatter." | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=3.8" | python-versions = ">=3.8" | ||||||
| files = [ | files = [ | ||||||
|     {file = "black-24.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6ad001a9ddd9b8dfd1b434d566be39b1cd502802c8d38bbb1ba612afda2ef436"}, |     {file = "black-24.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f7749fd0d97ff9415975a1432fac7df89bf13c3833cea079e55fa004d5f28c0"}, | ||||||
|     {file = "black-24.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3a3a092b8b756c643fe45f4624dbd5a389f770a4ac294cf4d0fce6af86addaf"}, |     {file = "black-24.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859f3cc5d2051adadf8fd504a01e02b0fd866d7549fff54bc9202d524d2e8bd7"}, | ||||||
|     {file = "black-24.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dae79397f367ac8d7adb6c779813328f6d690943f64b32983e896bcccd18cbad"}, |     {file = "black-24.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59271c9c29dfa97f7fda51f56c7809b3f78e72fd8d2205189bbd23022a0618b6"}, | ||||||
|     {file = "black-24.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:71d998b73c957444fb7c52096c3843875f4b6b47a54972598741fe9a7f737fcb"}, |     {file = "black-24.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:5ed9c34cba223149b5a0144951a0f33d65507cf82c5449cb3c35fe4b515fea9a"}, | ||||||
|     {file = "black-24.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8e5537f456a22cf5cfcb2707803431d2feeb82ab3748ade280d6ccd0b40ed2e8"}, |     {file = "black-24.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9dae3ae59d6f2dc93700fd5034a3115434686e66fd6e63d4dcaa48d19880f2b0"}, | ||||||
|     {file = "black-24.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64e60a7edd71fd542a10a9643bf369bfd2644de95ec71e86790b063aa02ff745"}, |     {file = "black-24.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5f8698974a81af83283eb47644f2711b5261138d6d9180c863fce673cbe04b13"}, | ||||||
|     {file = "black-24.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cd5b4f76056cecce3e69b0d4c228326d2595f506797f40b9233424e2524c070"}, |     {file = "black-24.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f404b6e77043b23d0321fb7772522b876b6de737ad3cb97d6b156638d68ce81"}, | ||||||
|     {file = "black-24.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:64578cf99b6b46a6301bc28bdb89f9d6f9b592b1c5837818a177c98525dbe397"}, |     {file = "black-24.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:c94e52b766477bdcd010b872ba0714d5458536dc9d0734eff6583ba7266ffd89"}, | ||||||
|     {file = "black-24.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f95cece33329dc4aa3b0e1a771c41075812e46cf3d6e3f1dfe3d91ff09826ed2"}, |     {file = "black-24.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:962d9e953872cdb83b97bb737ad47244ce2938054dc946685a4cad98520dab38"}, | ||||||
|     {file = "black-24.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4396ca365a4310beef84d446ca5016f671b10f07abdba3e4e4304218d2c71d33"}, |     {file = "black-24.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b1d8e3b2486b7dd522b1ab2ba1ec4907f0aa8f5e10a33c4271fb331d1d10b70c"}, | ||||||
|     {file = "black-24.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44d99dfdf37a2a00a6f7a8dcbd19edf361d056ee51093b2445de7ca09adac965"}, |     {file = "black-24.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed77e214b785148f57e43ca425b6e0850165144aa727d66ac604e56a70bb7825"}, | ||||||
|     {file = "black-24.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:21f9407063ec71c5580b8ad975653c66508d6a9f57bd008bb8691d273705adcd"}, |     {file = "black-24.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:4ef4764437d7eba8386689cd06e1fb5341ee0ae2e9e22582b21178782de7ed94"}, | ||||||
|     {file = "black-24.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:652e55bb722ca026299eb74e53880ee2315b181dfdd44dca98e43448620ddec1"}, |     {file = "black-24.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:92b183f8eef5baf7b20a513abcf982ad616f544f593f6688bb2850d2982911f1"}, | ||||||
|     {file = "black-24.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7f2966b9b2b3b7104fca9d75b2ee856fe3fdd7ed9e47c753a4bb1a675f2caab8"}, |     {file = "black-24.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:945abd7b3572add997757c94295bb3e73c6ffaf3366b1f26cb2356a4bffd1dc3"}, | ||||||
|     {file = "black-24.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bb9ca06e556a09f7f7177bc7cb604e5ed2d2df1e9119e4f7d2f1f7071c32e5d"}, |     {file = "black-24.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db5154b9e5b478031371d8bc41ff37b33855fa223a6cfba456c9b73fb96f77d4"}, | ||||||
|     {file = "black-24.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4e71cdebdc8efeb6deaf5f2deb28325f8614d48426bed118ecc2dcaefb9ebf3"}, |     {file = "black-24.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:afc84c33c1a9aaf3d73140cee776b4ddf73ff429ffe6b7c56dc1c9c10725856d"}, | ||||||
|     {file = "black-24.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6644f97a7ef6f401a150cca551a1ff97e03c25d8519ee0bbc9b0058772882665"}, |     {file = "black-24.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0889f4eb8b3bdf8b189e41a71cf0dbb8141a98346cd1a2695dea5995d416e940"}, | ||||||
|     {file = "black-24.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:75a2d0b4f5eb81f7eebc31f788f9830a6ce10a68c91fbe0fade34fff7a2836e6"}, |     {file = "black-24.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5bb0143f175db45a55227eefd63e90849d96c266330ba31719e9667d0d5ec3b9"}, | ||||||
|     {file = "black-24.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb949f56a63c5e134dfdca12091e98ffb5fd446293ebae123d10fc1abad00b9e"}, |     {file = "black-24.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:713a04a78e78f28ef7e8df7a16fe075670ea164860fcef3885e4f3dffc0184b3"}, | ||||||
|     {file = "black-24.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:7852b05d02b5b9a8c893ab95863ef8986e4dda29af80bbbda94d7aee1abf8702"}, |     {file = "black-24.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:171959bc879637a8cdbc53dc3fddae2a83e151937a28cf605fd175ce61e0e94a"}, | ||||||
|     {file = "black-24.4.0-py3-none-any.whl", hash = "sha256:74eb9b5420e26b42c00a3ff470dc0cd144b80a766128b1771d07643165e08d0e"}, |     {file = "black-24.4.1-py3-none-any.whl", hash = "sha256:ecbab810604fe02c70b3a08afd39beb599f7cc9afd13e81f5336014133b4fe35"}, | ||||||
|     {file = "black-24.4.0.tar.gz", hash = "sha256:f07b69fda20578367eaebbd670ff8fc653ab181e1ff95d84497f9fa20e7d0641"}, |     {file = "black-24.4.1.tar.gz", hash = "sha256:5241612dc8cad5b6fd47432b8bd04db80e07cfbc53bb69e9ae18985063bcb8dd"}, | ||||||
| ] | ] | ||||||
|  |  | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
| @ -3587,13 +3587,13 @@ django-query = ["django (>=3.2)"] | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "selenium" | name = "selenium" | ||||||
| version = "4.19.0" | version = "4.20.0" | ||||||
| description = "" | description = "" | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=3.8" | python-versions = ">=3.8" | ||||||
| files = [ | files = [ | ||||||
|     {file = "selenium-4.19.0-py3-none-any.whl", hash = "sha256:5b4f49240d61e687a73f7968ae2517d403882aae3550eae2a229c745e619f1d9"}, |     {file = "selenium-4.20.0-py3-none-any.whl", hash = "sha256:b1d0c33b38ca27d0499183e48e1dd09ff26973481f5d3ef2983073813ae6588d"}, | ||||||
|     {file = "selenium-4.19.0.tar.gz", hash = "sha256:d9dfd6d0b021d71d0a48b865fe7746490ba82b81e9c87b212360006629eb1853"}, |     {file = "selenium-4.20.0.tar.gz", hash = "sha256:0bd564ee166980d419a8aaf4ac00289bc152afcf2eadca5efe8c8e36711853fd"}, | ||||||
| ] | ] | ||||||
|  |  | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
|  | |||||||
							
								
								
									
										2034
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2034
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -46,7 +46,7 @@ | |||||||
|         "@open-wc/lit-helpers": "^0.7.0", |         "@open-wc/lit-helpers": "^0.7.0", | ||||||
|         "@patternfly/elements": "^3.0.1", |         "@patternfly/elements": "^3.0.1", | ||||||
|         "@patternfly/patternfly": "^4.224.2", |         "@patternfly/patternfly": "^4.224.2", | ||||||
|         "@sentry/browser": "^7.112.1", |         "@sentry/browser": "^7.112.2", | ||||||
|         "@webcomponents/webcomponentsjs": "^2.8.0", |         "@webcomponents/webcomponentsjs": "^2.8.0", | ||||||
|         "base64-js": "^1.5.1", |         "base64-js": "^1.5.1", | ||||||
|         "chart.js": "^4.4.2", |         "chart.js": "^4.4.2", | ||||||
|  | |||||||
| @ -214,28 +214,23 @@ export class IdentificationStageForm extends BaseStageForm<IdentificationStage> | |||||||
|                         name="sources" |                         name="sources" | ||||||
|                     > |                     > | ||||||
|                         <select class="pf-c-form-control" multiple> |                         <select class="pf-c-form-control" multiple> | ||||||
|                             ${this.sources?.results.map((source) => { |                             ${this.sources?.results | ||||||
|                                 let selected = Array.from(this.instance?.sources || []).some( |                                 .filter((source) => { | ||||||
|                                     (su) => { |                                     return source.component !== ""; | ||||||
|                                         return su == source.pk; |                                 }) | ||||||
|                                     }, |                                 .map((source) => { | ||||||
|                                 ); |                                     const selected = Array.from(this.instance?.sources || []).some( | ||||||
|                                 // Creating a new instance, auto-select built-in source |                                         (su) => { | ||||||
|                                 // Only when no other sources exist |                                             return su == source.pk; | ||||||
|                                 if ( |                                         }, | ||||||
|                                     !this.instance && |                                     ); | ||||||
|                                     source.component === "" && |                                     return html`<option | ||||||
|                                     (this.sources?.results || []).length < 2 |                                         value=${ifDefined(source.pk)} | ||||||
|                                 ) { |                                         ?selected=${selected} | ||||||
|                                     selected = true; |                                     > | ||||||
|                                 } |                                         ${source.name} | ||||||
|                                 return html`<option |                                     </option>`; | ||||||
|                                     value=${ifDefined(source.pk)} |                                 })} | ||||||
|                                     ?selected=${selected} |  | ||||||
|                                 > |  | ||||||
|                                     ${source.name} |  | ||||||
|                                 </option>`; |  | ||||||
|                             })} |  | ||||||
|                         </select> |                         </select> | ||||||
|                         <p class="pf-c-form__helper-text"> |                         <p class="pf-c-form__helper-text"> | ||||||
|                             ${msg( |                             ${msg( | ||||||
|  | |||||||
| @ -111,6 +111,21 @@ export function dateTimeLocal(date: Date): string { | |||||||
|     return `${parts[0]}:${parts[1]}`; |     return `${parts[0]}:${parts[1]}`; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export function dateToUTC(date: Date): Date { | ||||||
|  |     // Sigh...so our API is UTC/can take TZ info in the ISO format as it should. | ||||||
|  |     // datetime-local fields (which is almost the only date-time input we use) | ||||||
|  |     // can return its value as a UTC timestamp...however the generated API client | ||||||
|  |     // _requires_ a Date object, only to then convert it to an ISO string anyways | ||||||
|  |     // JS Dates don't include timezone info in the ISO string, so that just sends | ||||||
|  |     // the local time as UTC...which is wrong | ||||||
|  |     // Instead we have to do this, convert the given date to a UTC timestamp, | ||||||
|  |     // then subtract the timezone offset to create an "invalid" date (correct time&date) | ||||||
|  |     // but it still "thinks" it's in local TZ | ||||||
|  |     const timestamp = date.getTime(); | ||||||
|  |     const offset = -1 * (new Date().getTimezoneOffset() * 60000); | ||||||
|  |     return new Date(timestamp - offset); | ||||||
|  | } | ||||||
|  |  | ||||||
| // Lit is extremely well-typed with regard to CSS, and Storybook's `build` does not currently have a | // Lit is extremely well-typed with regard to CSS, and Storybook's `build` does not currently have a | ||||||
| // coherent way of importing CSS-as-text into CSSStyleSheet. It works well when Storybook is running | // coherent way of importing CSS-as-text into CSSStyleSheet. It works well when Storybook is running | ||||||
| // in `dev,` but in `build` it fails. Storied components will have to map their textual CSS imports | // in `dev,` but in `build` it fails. Storied components will have to map their textual CSS imports | ||||||
|  | |||||||
| @ -1,23 +1,22 @@ | |||||||
| import { EVENT_REFRESH } from "@goauthentik/authentik/common/constants"; | import { EVENT_REFRESH } from "@goauthentik/authentik/common/constants"; | ||||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||||
| import { authentikBrandContext } from "@goauthentik/elements/AuthentikContexts"; | import { authentikBrandContext } from "@goauthentik/elements/AuthentikContexts"; | ||||||
|  | import type { ReactiveElementHost } from "@goauthentik/elements/types.js"; | ||||||
|  |  | ||||||
| import { ContextProvider } from "@lit/context"; | import { ContextProvider } from "@lit/context"; | ||||||
| import { ReactiveController, ReactiveControllerHost } from "lit"; | import type { ReactiveController } from "lit"; | ||||||
|  |  | ||||||
| import type { CurrentBrand } from "@goauthentik/api"; | import type { CurrentBrand } from "@goauthentik/api"; | ||||||
| import { CoreApi } from "@goauthentik/api"; | import { CoreApi } from "@goauthentik/api"; | ||||||
|  |  | ||||||
| import type { AkInterface } from "./Interface"; | import type { AkInterface } from "./Interface"; | ||||||
|  |  | ||||||
| type ReactiveElementHost = Partial<ReactiveControllerHost> & AkInterface; |  | ||||||
|  |  | ||||||
| export class BrandContextController implements ReactiveController { | export class BrandContextController implements ReactiveController { | ||||||
|     host!: ReactiveElementHost; |     host!: ReactiveElementHost<AkInterface>; | ||||||
|  |  | ||||||
|     context!: ContextProvider<{ __context__: CurrentBrand | undefined }>; |     context!: ContextProvider<{ __context__: CurrentBrand | undefined }>; | ||||||
|  |  | ||||||
|     constructor(host: ReactiveElementHost) { |     constructor(host: ReactiveElementHost<AkInterface>) { | ||||||
|         this.host = host; |         this.host = host; | ||||||
|         this.context = new ContextProvider(this.host, { |         this.context = new ContextProvider(this.host, { | ||||||
|             context: authentikBrandContext, |             context: authentikBrandContext, | ||||||
|  | |||||||
| @ -2,23 +2,22 @@ import { EVENT_REFRESH } from "@goauthentik/authentik/common/constants"; | |||||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||||
| import { globalAK } from "@goauthentik/common/global"; | import { globalAK } from "@goauthentik/common/global"; | ||||||
| import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts"; | import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts"; | ||||||
|  | import type { ReactiveElementHost } from "@goauthentik/elements/types.js"; | ||||||
|  |  | ||||||
| import { ContextProvider } from "@lit/context"; | import { ContextProvider } from "@lit/context"; | ||||||
| import { ReactiveController, ReactiveControllerHost } from "lit"; | import type { ReactiveController } from "lit"; | ||||||
|  |  | ||||||
| import type { Config } from "@goauthentik/api"; | import type { Config } from "@goauthentik/api"; | ||||||
| import { RootApi } from "@goauthentik/api"; | import { RootApi } from "@goauthentik/api"; | ||||||
|  |  | ||||||
| import type { AkInterface } from "./Interface"; | import type { AkInterface } from "./Interface"; | ||||||
|  |  | ||||||
| type ReactiveElementHost = Partial<ReactiveControllerHost> & AkInterface; |  | ||||||
|  |  | ||||||
| export class ConfigContextController implements ReactiveController { | export class ConfigContextController implements ReactiveController { | ||||||
|     host!: ReactiveElementHost; |     host!: ReactiveElementHost<AkInterface>; | ||||||
|  |  | ||||||
|     context!: ContextProvider<{ __context__: Config | undefined }>; |     context!: ContextProvider<{ __context__: Config | undefined }>; | ||||||
|  |  | ||||||
|     constructor(host: ReactiveElementHost) { |     constructor(host: ReactiveElementHost<AkInterface>) { | ||||||
|         this.host = host; |         this.host = host; | ||||||
|         this.context = new ContextProvider(this.host, { |         this.context = new ContextProvider(this.host, { | ||||||
|             context: authentikConfigContext, |             context: authentikConfigContext, | ||||||
|  | |||||||
| @ -1,23 +1,22 @@ | |||||||
| import { EVENT_REFRESH_ENTERPRISE } from "@goauthentik/authentik/common/constants"; | import { EVENT_REFRESH_ENTERPRISE } from "@goauthentik/authentik/common/constants"; | ||||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||||
| import { authentikEnterpriseContext } from "@goauthentik/elements/AuthentikContexts"; | import { authentikEnterpriseContext } from "@goauthentik/elements/AuthentikContexts"; | ||||||
|  | import type { ReactiveElementHost } from "@goauthentik/elements/types.js"; | ||||||
|  |  | ||||||
| import { ContextProvider } from "@lit/context"; | import { ContextProvider } from "@lit/context"; | ||||||
| import { ReactiveController, ReactiveControllerHost } from "lit"; | import type { ReactiveController } from "lit"; | ||||||
|  |  | ||||||
| import type { LicenseSummary } from "@goauthentik/api"; | import type { LicenseSummary } from "@goauthentik/api"; | ||||||
| import { EnterpriseApi } from "@goauthentik/api"; | import { EnterpriseApi } from "@goauthentik/api"; | ||||||
|  |  | ||||||
| import type { AkEnterpriseInterface } from "./Interface"; | import type { AkEnterpriseInterface } from "./Interface"; | ||||||
|  |  | ||||||
| type ReactiveElementHost = Partial<ReactiveControllerHost> & AkEnterpriseInterface; |  | ||||||
|  |  | ||||||
| export class EnterpriseContextController implements ReactiveController { | export class EnterpriseContextController implements ReactiveController { | ||||||
|     host!: ReactiveElementHost; |     host!: ReactiveElementHost<AkEnterpriseInterface>; | ||||||
|  |  | ||||||
|     context!: ContextProvider<{ __context__: LicenseSummary | undefined }>; |     context!: ContextProvider<{ __context__: LicenseSummary | undefined }>; | ||||||
|  |  | ||||||
|     constructor(host: ReactiveElementHost) { |     constructor(host: ReactiveElementHost<AkEnterpriseInterface>) { | ||||||
|         this.host = host; |         this.host = host; | ||||||
|         this.context = new ContextProvider(this.host, { |         this.context = new ContextProvider(this.host, { | ||||||
|             context: authentikEnterpriseContext, |             context: authentikEnterpriseContext, | ||||||
|  | |||||||
| @ -1,13 +1,11 @@ | |||||||
| import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts"; | import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts"; | ||||||
|  | import type { Constructor } from "@goauthentik/elements/types.js"; | ||||||
|  |  | ||||||
| import { consume } from "@lit/context"; | import { consume } from "@lit/context"; | ||||||
| import type { LitElement } from "lit"; | import type { LitElement } from "lit"; | ||||||
|  |  | ||||||
| import type { Config } from "@goauthentik/api"; | import type { Config } from "@goauthentik/api"; | ||||||
|  |  | ||||||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any |  | ||||||
| type Constructor<T = object> = new (...args: any[]) => T; |  | ||||||
|  |  | ||||||
| export function WithAuthentikConfig<T extends Constructor<LitElement>>( | export function WithAuthentikConfig<T extends Constructor<LitElement>>( | ||||||
|     superclass: T, |     superclass: T, | ||||||
|     subscribe = true, |     subscribe = true, | ||||||
|  | |||||||
| @ -1,14 +1,12 @@ | |||||||
| import { authentikBrandContext } from "@goauthentik/elements/AuthentikContexts"; | import { authentikBrandContext } from "@goauthentik/elements/AuthentikContexts"; | ||||||
|  | import type { AbstractConstructor } from "@goauthentik/elements/types.js"; | ||||||
|  |  | ||||||
| import { consume } from "@lit/context"; | import { consume } from "@lit/context"; | ||||||
| import type { LitElement } from "lit"; | import type { LitElement } from "lit"; | ||||||
|  |  | ||||||
| import type { CurrentBrand } from "@goauthentik/api"; | import type { CurrentBrand } from "@goauthentik/api"; | ||||||
|  |  | ||||||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | export function WithBrandConfig<T extends AbstractConstructor<LitElement>>( | ||||||
| type Constructor<T = object> = abstract new (...args: any[]) => T; |  | ||||||
|  |  | ||||||
| export function WithBrandConfig<T extends Constructor<LitElement>>( |  | ||||||
|     superclass: T, |     superclass: T, | ||||||
|     subscribe = true, |     subscribe = true, | ||||||
| ) { | ) { | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts"; | import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts"; | ||||||
|  | import type { AbstractConstructor } from "@goauthentik/elements/types.js"; | ||||||
|  |  | ||||||
| import { consume } from "@lit/context"; | import { consume } from "@lit/context"; | ||||||
| import type { LitElement } from "lit"; | import type { LitElement } from "lit"; | ||||||
| @ -6,9 +7,6 @@ import type { LitElement } from "lit"; | |||||||
| import { CapabilitiesEnum } from "@goauthentik/api"; | import { CapabilitiesEnum } from "@goauthentik/api"; | ||||||
| import { Config } from "@goauthentik/api"; | import { Config } from "@goauthentik/api"; | ||||||
|  |  | ||||||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any |  | ||||||
| type Constructor<T = object> = abstract new (...args: any[]) => T; |  | ||||||
|  |  | ||||||
| // Using a unique, lexically scoped, and locally static symbol as the field name for the context | // Using a unique, lexically scoped, and locally static symbol as the field name for the context | ||||||
| // means that it's inaccessible to any child class looking for it. It's one of the strongest privacy | // means that it's inaccessible to any child class looking for it. It's one of the strongest privacy | ||||||
| // guarantees in JavaScript. | // guarantees in JavaScript. | ||||||
| @ -45,7 +43,7 @@ class WCC { | |||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| export function WithCapabilitiesConfig<T extends Constructor<LitElement>>( | export function WithCapabilitiesConfig<T extends AbstractConstructor<LitElement>>( | ||||||
|     superclass: T, |     superclass: T, | ||||||
|     subscribe = true, |     subscribe = true, | ||||||
| ) { | ) { | ||||||
|  | |||||||
| @ -1,13 +1,11 @@ | |||||||
| import { authentikEnterpriseContext } from "@goauthentik/elements/AuthentikContexts"; | import { authentikEnterpriseContext } from "@goauthentik/elements/AuthentikContexts"; | ||||||
|  | import { Constructor } from "@goauthentik/elements/types.js"; | ||||||
|  |  | ||||||
| import { consume } from "@lit/context"; | import { consume } from "@lit/context"; | ||||||
| import type { LitElement } from "lit"; | import type { LitElement } from "lit"; | ||||||
|  |  | ||||||
| import type { LicenseSummary } from "@goauthentik/api"; | import type { LicenseSummary } from "@goauthentik/api"; | ||||||
|  |  | ||||||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any |  | ||||||
| type Constructor<T = object> = abstract new (...args: any[]) => T; |  | ||||||
|  |  | ||||||
| export function WithLicenseSummary<T extends Constructor<LitElement>>( | export function WithLicenseSummary<T extends Constructor<LitElement>>( | ||||||
|     superclass: T, |     superclass: T, | ||||||
|     subscribe = true, |     subscribe = true, | ||||||
|  | |||||||
| @ -87,7 +87,7 @@ export class Markdown extends AKElement { | |||||||
|             const parsedContent = matter(this.md); |             const parsedContent = matter(this.md); | ||||||
|             const parsedHTML = this.converter.makeHtml(parsedContent.content); |             const parsedHTML = this.converter.makeHtml(parsedContent.content); | ||||||
|             const replacers = [...this.defaultReplacers, ...this.replacers]; |             const replacers = [...this.defaultReplacers, ...this.replacers]; | ||||||
|             this.docTitle = parsedContent.data["title"] ?? ""; |             this.docTitle = parsedContent?.data?.title ?? ""; | ||||||
|             this.docHtml = replacers.reduce( |             this.docHtml = replacers.reduce( | ||||||
|                 (html, replacer) => replacer(html, { path: this.meta }), |                 (html, replacer) => replacer(html, { path: this.meta }), | ||||||
|                 parsedHTML, |                 parsedHTML, | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import { EVENT_REFRESH } from "@goauthentik/common/constants"; | import { EVENT_REFRESH } from "@goauthentik/common/constants"; | ||||||
| import { MessageLevel } from "@goauthentik/common/messages"; | import { MessageLevel } from "@goauthentik/common/messages"; | ||||||
| import { camelToSnake, convertToSlug } from "@goauthentik/common/utils"; | import { camelToSnake, convertToSlug, dateToUTC } from "@goauthentik/common/utils"; | ||||||
| import { AKElement } from "@goauthentik/elements/Base"; | import { AKElement } from "@goauthentik/elements/Base"; | ||||||
| import { HorizontalFormElement } from "@goauthentik/elements/forms/HorizontalFormElement"; | import { HorizontalFormElement } from "@goauthentik/elements/forms/HorizontalFormElement"; | ||||||
| import { SearchSelect } from "@goauthentik/elements/forms/SearchSelect"; | import { SearchSelect } from "@goauthentik/elements/forms/SearchSelect"; | ||||||
| @ -104,7 +104,7 @@ export function serializeForm<T extends KeyUnknown>( | |||||||
|             inputElement.tagName.toLowerCase() === "input" && |             inputElement.tagName.toLowerCase() === "input" && | ||||||
|             inputElement.type === "datetime-local" |             inputElement.type === "datetime-local" | ||||||
|         ) { |         ) { | ||||||
|             assignValue(inputElement, new Date(inputElement.valueAsNumber), json); |             assignValue(inputElement, dateToUTC(new Date(inputElement.valueAsNumber)), json); | ||||||
|         } else if ( |         } else if ( | ||||||
|             inputElement.tagName.toLowerCase() === "input" && |             inputElement.tagName.toLowerCase() === "input" && | ||||||
|             "type" in inputElement.dataset && |             "type" in inputElement.dataset && | ||||||
| @ -112,7 +112,7 @@ export function serializeForm<T extends KeyUnknown>( | |||||||
|         ) { |         ) { | ||||||
|             // Workaround for Firefox <93, since 92 and older don't support |             // Workaround for Firefox <93, since 92 and older don't support | ||||||
|             // datetime-local fields |             // datetime-local fields | ||||||
|             assignValue(inputElement, new Date(inputElement.value), json); |             assignValue(inputElement, dateToUTC(new Date(inputElement.value)), json); | ||||||
|         } else if ( |         } else if ( | ||||||
|             inputElement.tagName.toLowerCase() === "input" && |             inputElement.tagName.toLowerCase() === "input" && | ||||||
|             inputElement.type === "checkbox" |             inputElement.type === "checkbox" | ||||||
|  | |||||||
							
								
								
									
										11
									
								
								web/src/elements/types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								web/src/elements/types.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | import { AKElement } from "@goauthentik/elements/Base"; | ||||||
|  |  | ||||||
|  | import { ReactiveControllerHost } from "lit"; | ||||||
|  |  | ||||||
|  | export type ReactiveElementHost<T = AKElement> = Partial<ReactiveControllerHost> & T; | ||||||
|  |  | ||||||
|  | // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||||||
|  | export type Constructor<T = object> = new (...args: any[]) => T; | ||||||
|  |  | ||||||
|  | // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||||||
|  | export type AbstractConstructor<T = object> = abstract new (...args: any[]) => T; | ||||||
| @ -1,3 +1,4 @@ | |||||||
|  | import { dateTimeLocal } from "@goauthentik/authentik/common/utils"; | ||||||
| import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; | ||||||
| import "@goauthentik/elements/forms/HorizontalFormElement"; | import "@goauthentik/elements/forms/HorizontalFormElement"; | ||||||
| import { ModelForm } from "@goauthentik/elements/forms/ModelForm"; | import { ModelForm } from "@goauthentik/elements/forms/ModelForm"; | ||||||
| @ -44,11 +45,8 @@ export class UserTokenForm extends ModelForm<Token, string> { | |||||||
|     renderForm(): TemplateResult { |     renderForm(): TemplateResult { | ||||||
|         const now = new Date(); |         const now = new Date(); | ||||||
|         const expiringDate = this.instance?.expires |         const expiringDate = this.instance?.expires | ||||||
|             ? new Date( |             ? new Date(this.instance.expires.getTime()) | ||||||
|                   this.instance.expires.getTime() - |             : new Date(now.getTime() + 30 * 60000); | ||||||
|                       this.instance.expires.getTimezoneOffset() * 60000, |  | ||||||
|               ) |  | ||||||
|             : new Date(now.getTime() + 30 * 60000 - now.getTimezoneOffset() * 60000); |  | ||||||
|  |  | ||||||
|         return html` <ak-form-element-horizontal |         return html` <ak-form-element-horizontal | ||||||
|                 label=${msg("Identifier")} |                 label=${msg("Identifier")} | ||||||
| @ -73,8 +71,8 @@ export class UserTokenForm extends ModelForm<Token, string> { | |||||||
|                 ? html`<ak-form-element-horizontal label=${msg("Expiring")} name="expires"> |                 ? html`<ak-form-element-horizontal label=${msg("Expiring")} name="expires"> | ||||||
|                       <input |                       <input | ||||||
|                           type="datetime-local" |                           type="datetime-local" | ||||||
|                           value="${expiringDate.toISOString().slice(0, -8)}" |                           value="${dateTimeLocal(expiringDate)}" | ||||||
|                           min="${now.toISOString().slice(0, -8)}" |                           min="${dateTimeLocal(now)}" | ||||||
|                           class="pf-c-form-control" |                           class="pf-c-form-control" | ||||||
|                       /> |                       /> | ||||||
|                   </ak-form-element-horizontal>` |                   </ak-form-element-horizontal>` | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Ken Sternberg
					Ken Sternberg