diff --git a/authentik/sources/plex/api/property_mappings.py b/authentik/sources/plex/api/property_mappings.py new file mode 100644 index 0000000000..80a9ac3e04 --- /dev/null +++ b/authentik/sources/plex/api/property_mappings.py @@ -0,0 +1,31 @@ +"""Plex source property mappings API""" + +from rest_framework.viewsets import ModelViewSet + +from authentik.core.api.property_mappings import PropertyMappingFilterSet, PropertyMappingSerializer +from authentik.core.api.used_by import UsedByMixin +from authentik.sources.plex.models import PlexSourcePropertyMapping + + +class PlexSourcePropertyMappingSerializer(PropertyMappingSerializer): + """PlexSourcePropertyMapping Serializer""" + + class Meta(PropertyMappingSerializer.Meta): + model = PlexSourcePropertyMapping + + +class PlexSourcePropertyMappingFilter(PropertyMappingFilterSet): + """Filter for PlexSourcePropertyMapping""" + + class Meta(PropertyMappingFilterSet.Meta): + model = PlexSourcePropertyMapping + + +class PlexSourcePropertyMappingViewSet(UsedByMixin, ModelViewSet): + """PlexSourcePropertyMapping Viewset""" + + queryset = PlexSourcePropertyMapping.objects.all() + serializer_class = PlexSourcePropertyMappingSerializer + filterset_class = PlexSourcePropertyMappingFilter + search_fields = ["name"] + ordering = ["name"] diff --git a/authentik/sources/plex/api/source.py b/authentik/sources/plex/api/source.py index 4560aea355..f662fe9f4b 100644 --- a/authentik/sources/plex/api/source.py +++ b/authentik/sources/plex/api/source.py @@ -19,7 +19,7 @@ from authentik.core.api.utils import PassiveSerializer from authentik.flows.challenge import RedirectChallenge from authentik.flows.views.executor import to_stage_response from authentik.rbac.decorators import permission_required -from authentik.sources.plex.models import PlexSource, PlexSourceConnection +from authentik.sources.plex.models import PlexSource, UserPlexSourceConnection from authentik.sources.plex.plex import PlexAuth, PlexSourceFlowManager LOGGER = get_logger() @@ -31,6 +31,7 @@ class PlexSourceSerializer(SourceSerializer): class Meta: model = PlexSource fields = SourceSerializer.Meta.fields + [ + "group_matching_mode", "client_id", "allowed_servers", "allow_friends", @@ -58,6 +59,7 @@ class PlexSourceViewSet(UsedByMixin, ModelViewSet): "enrollment_flow", "policy_engine_mode", "user_matching_mode", + "group_matching_mode", "client_id", "allow_friends", ] @@ -109,7 +111,10 @@ class PlexSourceViewSet(UsedByMixin, ModelViewSet): source=source, request=request, identifier=str(identifier), - user_info=user_info, + user_info={ + "info": user_info, + "auth_api": auth_api, + }, policy_context={}, ) return to_stage_response(request, sfm.get_flow(plex_token=plex_token)) @@ -158,7 +163,7 @@ class PlexSourceViewSet(UsedByMixin, ModelViewSet): friends_allowed = owner_api.check_friends_overlap(identifier) servers_allowed = auth_api.check_server_overlap() if any([friends_allowed, servers_allowed]): - PlexSourceConnection.objects.create( + UserPlexSourceConnection.objects.create( plex_token=plex_token, user=request.user, identifier=identifier, diff --git a/authentik/sources/plex/api/source_connection.py b/authentik/sources/plex/api/source_connection.py index 2b5dc13973..cc6ae57f7f 100644 --- a/authentik/sources/plex/api/source_connection.py +++ b/authentik/sources/plex/api/source_connection.py @@ -2,15 +2,20 @@ from rest_framework.viewsets import ModelViewSet -from authentik.core.api.sources import UserSourceConnectionSerializer, UserSourceConnectionViewSet -from authentik.sources.plex.models import PlexSourceConnection +from authentik.core.api.sources import ( + GroupSourceConnectionSerializer, + GroupSourceConnectionViewSet, + UserSourceConnectionSerializer, + UserSourceConnectionViewSet, +) +from authentik.sources.plex.models import GroupPlexSourceConnection, UserPlexSourceConnection -class PlexSourceConnectionSerializer(UserSourceConnectionSerializer): +class UserPlexSourceConnectionSerializer(UserSourceConnectionSerializer): """Plex Source connection Serializer""" class Meta(UserSourceConnectionSerializer.Meta): - model = PlexSourceConnection + model = UserPlexSourceConnection fields = UserSourceConnectionSerializer.Meta.fields + [ "identifier", "plex_token", @@ -21,8 +26,22 @@ class PlexSourceConnectionSerializer(UserSourceConnectionSerializer): } -class PlexSourceConnectionViewSet(UserSourceConnectionViewSet, ModelViewSet): +class UserPlexSourceConnectionViewSet(UserSourceConnectionViewSet, ModelViewSet): """Plex Source connection Serializer""" - queryset = PlexSourceConnection.objects.all() - serializer_class = PlexSourceConnectionSerializer + queryset = UserPlexSourceConnection.objects.all() + serializer_class = UserPlexSourceConnectionSerializer + + +class GroupPlexSourceConnectionSerializer(GroupSourceConnectionSerializer): + """Plex Group-Source connection Serializer""" + + class Meta(GroupSourceConnectionSerializer.Meta): + model = GroupPlexSourceConnection + + +class GroupPlexSourceConnectionViewSet(GroupSourceConnectionViewSet, ModelViewSet): + """Group-source connection Viewset""" + + queryset = GroupPlexSourceConnection.objects.all() + serializer_class = GroupPlexSourceConnectionSerializer diff --git a/authentik/sources/plex/migrations/0004_groupplexsourceconnection_plexsourcepropertymapping_and_more.py b/authentik/sources/plex/migrations/0004_groupplexsourceconnection_plexsourcepropertymapping_and_more.py new file mode 100644 index 0000000000..28e67f38b0 --- /dev/null +++ b/authentik/sources/plex/migrations/0004_groupplexsourceconnection_plexsourcepropertymapping_and_more.py @@ -0,0 +1,61 @@ +# Generated by Django 5.0.7 on 2024-08-05 11:29 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("authentik_core", "0039_source_group_matching_mode_alter_group_name_and_more"), + ("authentik_sources_plex", "0003_alter_plexsource_plex_token"), + ] + + operations = [ + migrations.CreateModel( + name="GroupPlexSourceConnection", + fields=[ + ( + "groupsourceconnection_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="authentik_core.groupsourceconnection", + ), + ), + ], + options={ + "verbose_name": "Group Plex Source Connection", + "verbose_name_plural": "Group Plex Source Connections", + }, + bases=("authentik_core.groupsourceconnection",), + ), + migrations.CreateModel( + name="PlexSourcePropertyMapping", + fields=[ + ( + "propertymapping_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="authentik_core.propertymapping", + ), + ), + ], + options={ + "verbose_name": "Plex Source Property Mapping", + "verbose_name_plural": "Plex Source Property Mappings", + }, + bases=("authentik_core.propertymapping",), + ), + migrations.RenameModel( + old_name="PlexSourceConnection", + new_name="UserPlexSourceConnection", + ), + ] diff --git a/authentik/sources/plex/models.py b/authentik/sources/plex/models.py index 12e5d92c47..92771014e2 100644 --- a/authentik/sources/plex/models.py +++ b/authentik/sources/plex/models.py @@ -1,5 +1,7 @@ """Plex source""" +from typing import Any + from django.contrib.postgres.fields import ArrayField from django.db import models from django.http.request import HttpRequest @@ -8,7 +10,12 @@ from django.utils.translation import gettext_lazy as _ from rest_framework.fields import CharField from rest_framework.serializers import BaseSerializer, Serializer -from authentik.core.models import Source, UserSourceConnection +from authentik.core.models import ( + GroupSourceConnection, + PropertyMapping, + Source, + UserSourceConnection, +) from authentik.core.types import UILoginButton, UserSettingSerializer from authentik.flows.challenge import Challenge, ChallengeResponse from authentik.lib.generators import generate_id @@ -60,6 +67,22 @@ class PlexSource(Source): return PlexSourceSerializer + @property + def property_mapping_type(self) -> type[PropertyMapping]: + return PlexSourcePropertyMapping + + def get_base_user_properties(self, info: dict[str, Any], **kwargs): + return { + "username": info.get("username"), + "email": info.get("email"), + "name": info.get("title"), + } + + def get_base_group_properties(self, group_id: str, **kwargs): + return { + "name": group_id, + } + @property def icon_url(self) -> str: icon = super().icon_url @@ -95,18 +118,52 @@ class PlexSource(Source): verbose_name_plural = _("Plex Sources") -class PlexSourceConnection(UserSourceConnection): +class PlexSourcePropertyMapping(PropertyMapping): + """Map Plex properties to User of Group object attributes""" + + @property + def component(self) -> str: + return "ak-property-mapping-plex-source-form" + + @property + def serializer(self) -> type[Serializer]: + from authentik.sources.plex.api.property_mappings import PlexSourcePropertyMappingSerializer + + return PlexSourcePropertyMappingSerializer + + class Meta: + verbose_name = _("Plex Source Property Mapping") + verbose_name_plural = _("Plex Source Property Mappings") + + +class UserPlexSourceConnection(UserSourceConnection): """Connect user and plex source""" plex_token = models.TextField() identifier = models.TextField() @property - def serializer(self) -> Serializer: - from authentik.sources.plex.api.source_connection import PlexSourceConnectionSerializer + def serializer(self) -> type[Serializer]: + from authentik.sources.plex.api.source_connection import UserPlexSourceConnectionSerializer - return PlexSourceConnectionSerializer + return UserPlexSourceConnectionSerializer class Meta: verbose_name = _("User Plex Source Connection") verbose_name_plural = _("User Plex Source Connections") + + +class GroupPlexSourceConnection(GroupSourceConnection): + """Group-source connection""" + + @property + def serializer(self) -> type[Serializer]: + from authentik.sources.plex.api.source_connection import ( + GroupPlexSourceConnectionSerializer, + ) + + return GroupPlexSourceConnectionSerializer + + class Meta: + verbose_name = _("Group Plex Source Connection") + verbose_name_plural = _("Group Plex Source Connections") diff --git a/authentik/sources/plex/plex.py b/authentik/sources/plex/plex.py index 60bc5e1c2b..4ac280905a 100644 --- a/authentik/sources/plex/plex.py +++ b/authentik/sources/plex/plex.py @@ -9,7 +9,7 @@ from structlog.stdlib import get_logger from authentik import __version__ from authentik.core.sources.flow_manager import SourceFlowManager from authentik.lib.utils.http import get_http_session -from authentik.sources.plex.models import PlexSource, PlexSourceConnection +from authentik.sources.plex.models import PlexSource, UserPlexSourceConnection LOGGER = get_logger() @@ -73,11 +73,7 @@ class PlexAuth: ) response.raise_for_status() raw_user_info = response.json() - return { - "username": raw_user_info.get("username"), - "email": raw_user_info.get("email"), - "name": raw_user_info.get("title"), - }, raw_user_info.get("id") + return raw_user_info, raw_user_info.get("id") def check_server_overlap(self) -> bool: """Check if the plex-token has any server overlap with our configured servers""" @@ -113,11 +109,11 @@ class PlexAuth: class PlexSourceFlowManager(SourceFlowManager): """Flow manager for plex sources""" - user_connection_type = PlexSourceConnection + user_connection_type = UserPlexSourceConnection def update_user_connection( - self, connection: PlexSourceConnection, **kwargs - ) -> PlexSourceConnection: + self, connection: UserPlexSourceConnection, **kwargs + ) -> UserPlexSourceConnection: """Set the access_token on the connection""" connection.plex_token = kwargs.get("plex_token") return connection diff --git a/authentik/sources/plex/tests.py b/authentik/sources/plex/tests.py index 3b78e714cf..53be3c77ce 100644 --- a/authentik/sources/plex/tests.py +++ b/authentik/sources/plex/tests.py @@ -54,7 +54,7 @@ class TestPlexSource(TestCase): self.assertEqual( api.get_user_info(), ( - {"username": "username", "email": "foo@bar.baz", "name": "title"}, + USER_INFO_RESPONSE, 1234123419, ), ) @@ -82,3 +82,21 @@ class TestPlexSource(TestCase): mocker.get("https://plex.tv/api/v2/user", exc=RequestException()) check_plex_token_all() self.assertTrue(Event.objects.filter(action=EventAction.CONFIGURATION_ERROR).exists()) + + def test_user_base_properties(self): + """Test user base properties""" + properties = self.source.get_base_user_properties(info=USER_INFO_RESPONSE) + self.assertEqual( + properties, + { + "username": "username", + "name": "title", + "email": "foo@bar.baz", + }, + ) + + def test_group_base_properties(self): + """Test group base properties""" + for group_id in ["group 1", "group 2"]: + properties = self.source.get_base_group_properties(group_id=group_id) + self.assertEqual(properties, {"name": group_id}) diff --git a/authentik/sources/plex/urls.py b/authentik/sources/plex/urls.py index d1fa679dad..2460d52a4e 100644 --- a/authentik/sources/plex/urls.py +++ b/authentik/sources/plex/urls.py @@ -1,9 +1,15 @@ """API URLs""" +from authentik.sources.plex.api.property_mappings import PlexSourcePropertyMappingViewSet from authentik.sources.plex.api.source import PlexSourceViewSet -from authentik.sources.plex.api.source_connection import PlexSourceConnectionViewSet +from authentik.sources.plex.api.source_connection import ( + GroupPlexSourceConnectionViewSet, + UserPlexSourceConnectionViewSet, +) api_urlpatterns = [ - ("sources/user_connections/plex", PlexSourceConnectionViewSet), + ("propertymappings/source/plex", PlexSourcePropertyMappingViewSet), + ("sources/user_connections/plex", UserPlexSourceConnectionViewSet), + ("sources/group_connections/plex", GroupPlexSourceConnectionViewSet), ("sources/plex", PlexSourceViewSet), ] diff --git a/blueprints/schema.json b/blueprints/schema.json index 8ae6d8156f..ec03caedef 100644 --- a/blueprints/schema.json +++ b/blueprints/schema.json @@ -1369,7 +1369,7 @@ ], "properties": { "model": { - "const": "authentik_sources_plex.plexsourceconnection" + "const": "authentik_sources_plex.plexsourcepropertymapping" }, "id": { "type": "string" @@ -1391,13 +1391,93 @@ } }, "permissions": { - "$ref": "#/$defs/model_authentik_sources_plex.plexsourceconnection_permissions" + "$ref": "#/$defs/model_authentik_sources_plex.plexsourcepropertymapping_permissions" }, "attrs": { - "$ref": "#/$defs/model_authentik_sources_plex.plexsourceconnection" + "$ref": "#/$defs/model_authentik_sources_plex.plexsourcepropertymapping" }, "identifiers": { - "$ref": "#/$defs/model_authentik_sources_plex.plexsourceconnection" + "$ref": "#/$defs/model_authentik_sources_plex.plexsourcepropertymapping" + } + } + }, + { + "type": "object", + "required": [ + "model", + "identifiers" + ], + "properties": { + "model": { + "const": "authentik_sources_plex.userplexsourceconnection" + }, + "id": { + "type": "string" + }, + "state": { + "type": "string", + "enum": [ + "absent", + "present", + "created", + "must_created" + ], + "default": "present" + }, + "conditions": { + "type": "array", + "items": { + "type": "boolean" + } + }, + "permissions": { + "$ref": "#/$defs/model_authentik_sources_plex.userplexsourceconnection_permissions" + }, + "attrs": { + "$ref": "#/$defs/model_authentik_sources_plex.userplexsourceconnection" + }, + "identifiers": { + "$ref": "#/$defs/model_authentik_sources_plex.userplexsourceconnection" + } + } + }, + { + "type": "object", + "required": [ + "model", + "identifiers" + ], + "properties": { + "model": { + "const": "authentik_sources_plex.groupplexsourceconnection" + }, + "id": { + "type": "string" + }, + "state": { + "type": "string", + "enum": [ + "absent", + "present", + "created", + "must_created" + ], + "default": "present" + }, + "conditions": { + "type": "array", + "items": { + "type": "boolean" + } + }, + "permissions": { + "$ref": "#/$defs/model_authentik_sources_plex.groupplexsourceconnection_permissions" + }, + "attrs": { + "$ref": "#/$defs/model_authentik_sources_plex.groupplexsourceconnection" + }, + "identifiers": { + "$ref": "#/$defs/model_authentik_sources_plex.groupplexsourceconnection" } } }, @@ -4270,7 +4350,9 @@ "authentik_sources_oauth.useroauthsourceconnection", "authentik_sources_oauth.groupoauthsourceconnection", "authentik_sources_plex.plexsource", - "authentik_sources_plex.plexsourceconnection", + "authentik_sources_plex.plexsourcepropertymapping", + "authentik_sources_plex.userplexsourceconnection", + "authentik_sources_plex.groupplexsourceconnection", "authentik_sources_saml.samlsource", "authentik_sources_saml.samlsourcepropertymapping", "authentik_sources_saml.usersamlsourceconnection", @@ -5966,6 +6048,10 @@ "authentik_core.delete_group", "authentik_core.remove_user_from_group", "authentik_core.view_group", + "authentik_core.add_groupsourceconnection", + "authentik_core.change_groupsourceconnection", + "authentik_core.delete_groupsourceconnection", + "authentik_core.view_groupsourceconnection", "authentik_core.add_propertymapping", "authentik_core.change_propertymapping", "authentik_core.delete_propertymapping", @@ -6235,6 +6321,22 @@ "authentik_rbac.edit_system_settings", "authentik_rbac.view_system_info", "authentik_rbac.view_system_settings", + "authentik_sources_kerberos.add_groupkerberossourceconnection", + "authentik_sources_kerberos.change_groupkerberossourceconnection", + "authentik_sources_kerberos.delete_groupkerberossourceconnection", + "authentik_sources_kerberos.view_groupkerberossourceconnection", + "authentik_sources_kerberos.add_kerberospropertymapping", + "authentik_sources_kerberos.change_kerberospropertymapping", + "authentik_sources_kerberos.delete_kerberospropertymapping", + "authentik_sources_kerberos.view_kerberospropertymapping", + "authentik_sources_kerberos.add_kerberossource", + "authentik_sources_kerberos.change_kerberossource", + "authentik_sources_kerberos.delete_kerberossource", + "authentik_sources_kerberos.view_kerberossource", + "authentik_sources_kerberos.add_userkerberossourceconnection", + "authentik_sources_kerberos.change_userkerberossourceconnection", + "authentik_sources_kerberos.delete_userkerberossourceconnection", + "authentik_sources_kerberos.view_userkerberossourceconnection", "authentik_sources_ldap.add_ldapsource", "authentik_sources_ldap.change_ldapsource", "authentik_sources_ldap.delete_ldapsource", @@ -6243,10 +6345,18 @@ "authentik_sources_ldap.change_ldapsourcepropertymapping", "authentik_sources_ldap.delete_ldapsourcepropertymapping", "authentik_sources_ldap.view_ldapsourcepropertymapping", + "authentik_sources_oauth.add_groupoauthsourceconnection", + "authentik_sources_oauth.change_groupoauthsourceconnection", + "authentik_sources_oauth.delete_groupoauthsourceconnection", + "authentik_sources_oauth.view_groupoauthsourceconnection", "authentik_sources_oauth.add_oauthsource", "authentik_sources_oauth.change_oauthsource", "authentik_sources_oauth.delete_oauthsource", "authentik_sources_oauth.view_oauthsource", + "authentik_sources_oauth.add_oauthsourcepropertymapping", + "authentik_sources_oauth.change_oauthsourcepropertymapping", + "authentik_sources_oauth.delete_oauthsourcepropertymapping", + "authentik_sources_oauth.view_oauthsourcepropertymapping", "authentik_sources_oauth.add_useroauthsourceconnection", "authentik_sources_oauth.change_useroauthsourceconnection", "authentik_sources_oauth.delete_useroauthsourceconnection", @@ -6259,10 +6369,18 @@ "authentik_sources_plex.change_plexsourceconnection", "authentik_sources_plex.delete_plexsourceconnection", "authentik_sources_plex.view_plexsourceconnection", + "authentik_sources_saml.add_groupsamlsourceconnection", + "authentik_sources_saml.change_groupsamlsourceconnection", + "authentik_sources_saml.delete_groupsamlsourceconnection", + "authentik_sources_saml.view_groupsamlsourceconnection", "authentik_sources_saml.add_samlsource", "authentik_sources_saml.change_samlsource", "authentik_sources_saml.delete_samlsource", "authentik_sources_saml.view_samlsource", + "authentik_sources_saml.add_samlsourcepropertymapping", + "authentik_sources_saml.change_samlsourcepropertymapping", + "authentik_sources_saml.delete_samlsourcepropertymapping", + "authentik_sources_saml.view_samlsourcepropertymapping", "authentik_sources_saml.add_usersamlsourceconnection", "authentik_sources_saml.change_usersamlsourceconnection", "authentik_sources_saml.delete_usersamlsourceconnection", @@ -7118,6 +7236,16 @@ "minLength": 1, "title": "Icon" }, + "group_matching_mode": { + "type": "string", + "enum": [ + "identifier", + "name_link", + "name_deny" + ], + "title": "Group matching mode", + "description": "How the source determines if an existing group should be used or a new group created." + }, "client_id": { "type": "string", "minLength": 1, @@ -7174,7 +7302,58 @@ } } }, - "model_authentik_sources_plex.plexsourceconnection": { + "model_authentik_sources_plex.plexsourcepropertymapping": { + "type": "object", + "properties": { + "managed": { + "type": [ + "string", + "null" + ], + "minLength": 1, + "title": "Managed by authentik", + "description": "Objects that are managed by authentik. These objects are created and updated automatically. This flag only indicates that an object can be overwritten by migrations. You can still modify the objects via the API, but expect changes to be overwritten in a later update." + }, + "name": { + "type": "string", + "minLength": 1, + "title": "Name" + }, + "expression": { + "type": "string", + "minLength": 1, + "title": "Expression" + } + }, + "required": [] + }, + "model_authentik_sources_plex.plexsourcepropertymapping_permissions": { + "type": "array", + "items": { + "type": "object", + "required": [ + "permission" + ], + "properties": { + "permission": { + "type": "string", + "enum": [ + "add_plexsourcepropertymapping", + "change_plexsourcepropertymapping", + "delete_plexsourcepropertymapping", + "view_plexsourcepropertymapping" + ] + }, + "user": { + "type": "integer" + }, + "role": { + "type": "string" + } + } + } + }, + "model_authentik_sources_plex.userplexsourceconnection": { "type": "object", "properties": { "identifier": { @@ -7195,7 +7374,7 @@ }, "required": [] }, - "model_authentik_sources_plex.plexsourceconnection_permissions": { + "model_authentik_sources_plex.userplexsourceconnection_permissions": { "type": "array", "items": { "type": "object", @@ -7206,10 +7385,47 @@ "permission": { "type": "string", "enum": [ - "add_plexsourceconnection", - "change_plexsourceconnection", - "delete_plexsourceconnection", - "view_plexsourceconnection" + "add_userplexsourceconnection", + "change_userplexsourceconnection", + "delete_userplexsourceconnection", + "view_userplexsourceconnection" + ] + }, + "user": { + "type": "integer" + }, + "role": { + "type": "string" + } + } + } + }, + "model_authentik_sources_plex.groupplexsourceconnection": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "minLength": 1, + "title": "Icon" + } + }, + "required": [] + }, + "model_authentik_sources_plex.groupplexsourceconnection_permissions": { + "type": "array", + "items": { + "type": "object", + "required": [ + "permission" + ], + "properties": { + "permission": { + "type": "string", + "enum": [ + "add_groupplexsourceconnection", + "change_groupplexsourceconnection", + "delete_groupplexsourceconnection", + "view_groupplexsourceconnection" ] }, "user": { @@ -11495,6 +11711,10 @@ "authentik_core.delete_group", "authentik_core.remove_user_from_group", "authentik_core.view_group", + "authentik_core.add_groupsourceconnection", + "authentik_core.change_groupsourceconnection", + "authentik_core.delete_groupsourceconnection", + "authentik_core.view_groupsourceconnection", "authentik_core.add_propertymapping", "authentik_core.change_propertymapping", "authentik_core.delete_propertymapping", @@ -11764,6 +11984,22 @@ "authentik_rbac.edit_system_settings", "authentik_rbac.view_system_info", "authentik_rbac.view_system_settings", + "authentik_sources_kerberos.add_groupkerberossourceconnection", + "authentik_sources_kerberos.change_groupkerberossourceconnection", + "authentik_sources_kerberos.delete_groupkerberossourceconnection", + "authentik_sources_kerberos.view_groupkerberossourceconnection", + "authentik_sources_kerberos.add_kerberospropertymapping", + "authentik_sources_kerberos.change_kerberospropertymapping", + "authentik_sources_kerberos.delete_kerberospropertymapping", + "authentik_sources_kerberos.view_kerberospropertymapping", + "authentik_sources_kerberos.add_kerberossource", + "authentik_sources_kerberos.change_kerberossource", + "authentik_sources_kerberos.delete_kerberossource", + "authentik_sources_kerberos.view_kerberossource", + "authentik_sources_kerberos.add_userkerberossourceconnection", + "authentik_sources_kerberos.change_userkerberossourceconnection", + "authentik_sources_kerberos.delete_userkerberossourceconnection", + "authentik_sources_kerberos.view_userkerberossourceconnection", "authentik_sources_ldap.add_ldapsource", "authentik_sources_ldap.change_ldapsource", "authentik_sources_ldap.delete_ldapsource", @@ -11772,10 +12008,18 @@ "authentik_sources_ldap.change_ldapsourcepropertymapping", "authentik_sources_ldap.delete_ldapsourcepropertymapping", "authentik_sources_ldap.view_ldapsourcepropertymapping", + "authentik_sources_oauth.add_groupoauthsourceconnection", + "authentik_sources_oauth.change_groupoauthsourceconnection", + "authentik_sources_oauth.delete_groupoauthsourceconnection", + "authentik_sources_oauth.view_groupoauthsourceconnection", "authentik_sources_oauth.add_oauthsource", "authentik_sources_oauth.change_oauthsource", "authentik_sources_oauth.delete_oauthsource", "authentik_sources_oauth.view_oauthsource", + "authentik_sources_oauth.add_oauthsourcepropertymapping", + "authentik_sources_oauth.change_oauthsourcepropertymapping", + "authentik_sources_oauth.delete_oauthsourcepropertymapping", + "authentik_sources_oauth.view_oauthsourcepropertymapping", "authentik_sources_oauth.add_useroauthsourceconnection", "authentik_sources_oauth.change_useroauthsourceconnection", "authentik_sources_oauth.delete_useroauthsourceconnection", @@ -11788,10 +12032,18 @@ "authentik_sources_plex.change_plexsourceconnection", "authentik_sources_plex.delete_plexsourceconnection", "authentik_sources_plex.view_plexsourceconnection", + "authentik_sources_saml.add_groupsamlsourceconnection", + "authentik_sources_saml.change_groupsamlsourceconnection", + "authentik_sources_saml.delete_groupsamlsourceconnection", + "authentik_sources_saml.view_groupsamlsourceconnection", "authentik_sources_saml.add_samlsource", "authentik_sources_saml.change_samlsource", "authentik_sources_saml.delete_samlsource", "authentik_sources_saml.view_samlsource", + "authentik_sources_saml.add_samlsourcepropertymapping", + "authentik_sources_saml.change_samlsourcepropertymapping", + "authentik_sources_saml.delete_samlsourcepropertymapping", + "authentik_sources_saml.view_samlsourcepropertymapping", "authentik_sources_saml.add_usersamlsourceconnection", "authentik_sources_saml.change_usersamlsourceconnection", "authentik_sources_saml.delete_usersamlsourceconnection", diff --git a/schema.yml b/schema.yml index e504873ea4..e5c8013ffc 100644 --- a/schema.yml +++ b/schema.yml @@ -16540,6 +16540,287 @@ paths: schema: $ref: '#/components/schemas/GenericError' description: '' + /propertymappings/source/plex/: + get: + operationId: propertymappings_source_plex_list + description: PlexSourcePropertyMapping Viewset + parameters: + - in: query + name: managed + schema: + type: array + items: + type: string + explode: true + style: form + - in: query + name: managed__isnull + schema: + type: boolean + - in: query + name: name + schema: + type: string + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + - name: page + required: false + in: query + description: A page number within the paginated result set. + schema: + type: integer + - name: page_size + required: false + in: query + description: Number of results to return per page. + schema: + type: integer + - name: search + required: false + in: query + description: A search term. + schema: + type: string + tags: + - propertymappings + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedPlexSourcePropertyMappingList' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + post: + operationId: propertymappings_source_plex_create + description: PlexSourcePropertyMapping Viewset + tags: + - propertymappings + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PlexSourcePropertyMappingRequest' + required: true + security: + - authentik: [] + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/PlexSourcePropertyMapping' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + /propertymappings/source/plex/{pm_uuid}/: + get: + operationId: propertymappings_source_plex_retrieve + description: PlexSourcePropertyMapping Viewset + parameters: + - in: path + name: pm_uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Plex Source Property Mapping. + required: true + tags: + - propertymappings + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PlexSourcePropertyMapping' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + put: + operationId: propertymappings_source_plex_update + description: PlexSourcePropertyMapping Viewset + parameters: + - in: path + name: pm_uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Plex Source Property Mapping. + required: true + tags: + - propertymappings + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PlexSourcePropertyMappingRequest' + required: true + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PlexSourcePropertyMapping' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + patch: + operationId: propertymappings_source_plex_partial_update + description: PlexSourcePropertyMapping Viewset + parameters: + - in: path + name: pm_uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Plex Source Property Mapping. + required: true + tags: + - propertymappings + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PatchedPlexSourcePropertyMappingRequest' + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PlexSourcePropertyMapping' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + delete: + operationId: propertymappings_source_plex_destroy + description: PlexSourcePropertyMapping Viewset + parameters: + - in: path + name: pm_uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Plex Source Property Mapping. + required: true + tags: + - propertymappings + security: + - authentik: [] + responses: + '204': + description: No response body + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + /propertymappings/source/plex/{pm_uuid}/used_by/: + get: + operationId: propertymappings_source_plex_used_by_list + description: Get a list of all objects that use this object + parameters: + - in: path + name: pm_uuid + schema: + type: string + format: uuid + description: A UUID string identifying this Plex Source Property Mapping. + required: true + tags: + - propertymappings + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/UsedBy' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' /propertymappings/source/saml/: get: operationId: propertymappings_source_saml_list @@ -22175,8 +22456,10 @@ paths: - authentik_sources_oauth.oauthsource - authentik_sources_oauth.oauthsourcepropertymapping - authentik_sources_oauth.useroauthsourceconnection + - authentik_sources_plex.groupplexsourceconnection - authentik_sources_plex.plexsource - - authentik_sources_plex.plexsourceconnection + - authentik_sources_plex.plexsourcepropertymapping + - authentik_sources_plex.userplexsourceconnection - authentik_sources_saml.groupsamlsourceconnection - authentik_sources_saml.samlsource - authentik_sources_saml.samlsourcepropertymapping @@ -22407,8 +22690,10 @@ paths: - authentik_sources_oauth.oauthsource - authentik_sources_oauth.oauthsourcepropertymapping - authentik_sources_oauth.useroauthsourceconnection + - authentik_sources_plex.groupplexsourceconnection - authentik_sources_plex.plexsource - - authentik_sources_plex.plexsourceconnection + - authentik_sources_plex.plexsourcepropertymapping + - authentik_sources_plex.userplexsourceconnection - authentik_sources_saml.groupsamlsourceconnection - authentik_sources_saml.samlsource - authentik_sources_saml.samlsourcepropertymapping @@ -23958,6 +24243,258 @@ paths: schema: $ref: '#/components/schemas/GenericError' description: '' + /sources/group_connections/plex/: + get: + operationId: sources_group_connections_plex_list + description: Group-source connection Viewset + parameters: + - in: query + name: group + schema: + type: string + format: uuid + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + - name: page + required: false + in: query + description: A page number within the paginated result set. + schema: + type: integer + - name: page_size + required: false + in: query + description: Number of results to return per page. + schema: + type: integer + - name: search + required: false + in: query + description: A search term. + schema: + type: string + - in: query + name: source__slug + schema: + type: string + tags: + - sources + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedGroupPlexSourceConnectionList' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + post: + operationId: sources_group_connections_plex_create + description: Group-source connection Viewset + tags: + - sources + security: + - authentik: [] + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/GroupPlexSourceConnection' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + /sources/group_connections/plex/{id}/: + get: + operationId: sources_group_connections_plex_retrieve + description: Group-source connection Viewset + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this Group Plex Source Connection. + required: true + tags: + - sources + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/GroupPlexSourceConnection' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + put: + operationId: sources_group_connections_plex_update + description: Group-source connection Viewset + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this Group Plex Source Connection. + required: true + tags: + - sources + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/GroupPlexSourceConnection' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + patch: + operationId: sources_group_connections_plex_partial_update + description: Group-source connection Viewset + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this Group Plex Source Connection. + required: true + tags: + - sources + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/GroupPlexSourceConnection' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + delete: + operationId: sources_group_connections_plex_destroy + description: Group-source connection Viewset + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this Group Plex Source Connection. + required: true + tags: + - sources + security: + - authentik: [] + responses: + '204': + description: No response body + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' + /sources/group_connections/plex/{id}/used_by/: + get: + operationId: sources_group_connections_plex_used_by_list + description: Get a list of all objects that use this object + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this Group Plex Source Connection. + required: true + tags: + - sources + security: + - authentik: [] + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/UsedBy' + description: '' + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationError' + description: '' + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/GenericError' + description: '' /sources/group_connections/saml/: get: operationId: sources_group_connections_saml_list @@ -25026,6 +25563,17 @@ paths: schema: type: string format: uuid + - in: query + name: group_matching_mode + schema: + type: string + enum: + - identifier + - name_deny + - name_link + description: |+ + How the source determines if an existing group should be used or a new group created. + - in: query name: name schema: @@ -27139,7 +27687,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PaginatedPlexSourceConnectionList' + $ref: '#/components/schemas/PaginatedUserPlexSourceConnectionList' description: '' '400': content: @@ -27162,7 +27710,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PlexSourceConnectionRequest' + $ref: '#/components/schemas/UserPlexSourceConnectionRequest' required: true security: - authentik: [] @@ -27171,7 +27719,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PlexSourceConnection' + $ref: '#/components/schemas/UserPlexSourceConnection' description: '' '400': content: @@ -27205,7 +27753,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PlexSourceConnection' + $ref: '#/components/schemas/UserPlexSourceConnection' description: '' '400': content: @@ -27235,7 +27783,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PlexSourceConnectionRequest' + $ref: '#/components/schemas/UserPlexSourceConnectionRequest' required: true security: - authentik: [] @@ -27244,7 +27792,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PlexSourceConnection' + $ref: '#/components/schemas/UserPlexSourceConnection' description: '' '400': content: @@ -27274,7 +27822,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PatchedPlexSourceConnectionRequest' + $ref: '#/components/schemas/PatchedUserPlexSourceConnectionRequest' security: - authentik: [] responses: @@ -27282,7 +27830,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PlexSourceConnection' + $ref: '#/components/schemas/UserPlexSourceConnection' description: '' '400': content: @@ -39635,6 +40183,35 @@ components: - identifier - pk - source + GroupPlexSourceConnection: + type: object + description: Plex Group-Source connection Serializer + properties: + pk: + type: integer + readOnly: true + title: ID + group: + type: string + format: uuid + readOnly: true + source: + allOf: + - $ref: '#/components/schemas/Source' + readOnly: true + identifier: + type: string + readOnly: true + created: + type: string + format: date-time + readOnly: true + required: + - created + - group + - identifier + - pk + - source GroupRequest: type: object description: Group Serializer @@ -41235,7 +41812,9 @@ components: - authentik_sources_oauth.useroauthsourceconnection - authentik_sources_oauth.groupoauthsourceconnection - authentik_sources_plex.plexsource - - authentik_sources_plex.plexsourceconnection + - authentik_sources_plex.plexsourcepropertymapping + - authentik_sources_plex.userplexsourceconnection + - authentik_sources_plex.groupplexsourceconnection - authentik_sources_saml.samlsource - authentik_sources_saml.samlsourcepropertymapping - authentik_sources_saml.usersamlsourceconnection @@ -42764,6 +43343,18 @@ components: required: - pagination - results + PaginatedGroupPlexSourceConnectionList: + type: object + properties: + pagination: + $ref: '#/components/schemas/Pagination' + results: + type: array + items: + $ref: '#/components/schemas/GroupPlexSourceConnection' + required: + - pagination + - results PaginatedGroupSAMLSourceConnectionList: type: object properties: @@ -43076,18 +43667,6 @@ components: required: - pagination - results - PaginatedPlexSourceConnectionList: - type: object - properties: - pagination: - $ref: '#/components/schemas/Pagination' - results: - type: array - items: - $ref: '#/components/schemas/PlexSourceConnection' - required: - - pagination - - results PaginatedPlexSourceList: type: object properties: @@ -43100,6 +43679,18 @@ components: required: - pagination - results + PaginatedPlexSourcePropertyMappingList: + type: object + properties: + pagination: + $ref: '#/components/schemas/Pagination' + results: + type: array + items: + $ref: '#/components/schemas/PlexSourcePropertyMapping' + required: + - pagination + - results PaginatedPolicyBindingList: type: object properties: @@ -43676,6 +44267,18 @@ components: required: - pagination - results + PaginatedUserPlexSourceConnectionList: + type: object + properties: + pagination: + $ref: '#/components/schemas/Pagination' + results: + type: array + items: + $ref: '#/components/schemas/UserPlexSourceConnection' + required: + - pagination + - results PaginatedUserSAMLSourceConnectionList: type: object properties: @@ -45733,16 +46336,24 @@ components: object_pk: type: string minLength: 1 - PatchedPlexSourceConnectionRequest: + PatchedPlexSourcePropertyMappingRequest: type: object - description: Plex Source connection Serializer + description: PlexSourcePropertyMapping Serializer properties: - identifier: + managed: + type: string + nullable: true + minLength: 1 + title: Managed by authentik + description: Objects that are managed by authentik. These objects are created + and updated automatically. This flag only indicates that an object can + be overwritten by migrations. You can still modify the objects via the + API, but expect changes to be overwritten in a later update. + name: type: string minLength: 1 - plex_token: + expression: type: string - writeOnly: true minLength: 1 PatchedPlexSourceRequest: type: object @@ -45790,6 +46401,11 @@ components: user_path_template: type: string minLength: 1 + group_matching_mode: + allOf: + - $ref: '#/components/schemas/GroupMatchingModeEnum' + description: How the source determines if an existing group should be used + or a new group created. client_id: type: string minLength: 1 @@ -46719,6 +47335,17 @@ components: type: string writeOnly: true nullable: true + PatchedUserPlexSourceConnectionRequest: + type: object + description: Plex Source connection Serializer + properties: + identifier: + type: string + minLength: 1 + plex_token: + type: string + writeOnly: true + minLength: 1 PatchedUserRequest: type: object description: User Serializer @@ -46963,6 +47590,11 @@ components: icon: type: string readOnly: true + group_matching_mode: + allOf: + - $ref: '#/components/schemas/GroupMatchingModeEnum' + description: How the source determines if an existing group should be used + or a new group created. client_id: type: string description: Client identifier used to talk to Plex. @@ -46989,47 +47621,73 @@ components: - slug - verbose_name - verbose_name_plural - PlexSourceConnection: + PlexSourcePropertyMapping: type: object - description: Plex Source connection Serializer + description: PlexSourcePropertyMapping Serializer properties: pk: - type: integer - readOnly: true - title: ID - user: - type: integer - readOnly: true - source: - allOf: - - $ref: '#/components/schemas/Source' - readOnly: true - created: type: string - format: date-time + format: uuid readOnly: true - identifier: + title: Pm uuid + managed: type: string + nullable: true + title: Managed by authentik + description: Objects that are managed by authentik. These objects are created + and updated automatically. This flag only indicates that an object can + be overwritten by migrations. You can still modify the objects via the + API, but expect changes to be overwritten in a later update. + name: + type: string + expression: + type: string + component: + type: string + description: Get object's component so that we know how to edit the object + readOnly: true + verbose_name: + type: string + description: Return object's verbose_name + readOnly: true + verbose_name_plural: + type: string + description: Return object's plural verbose_name + readOnly: true + meta_model_name: + type: string + description: Return internal model name + readOnly: true required: - - created - - identifier + - component + - expression + - meta_model_name + - name - pk - - source - - user - PlexSourceConnectionRequest: + - verbose_name + - verbose_name_plural + PlexSourcePropertyMappingRequest: type: object - description: Plex Source connection Serializer + description: PlexSourcePropertyMapping Serializer properties: - identifier: + managed: + type: string + nullable: true + minLength: 1 + title: Managed by authentik + description: Objects that are managed by authentik. These objects are created + and updated automatically. This flag only indicates that an object can + be overwritten by migrations. You can still modify the objects via the + API, but expect changes to be overwritten in a later update. + name: type: string minLength: 1 - plex_token: + expression: type: string - writeOnly: true minLength: 1 required: - - identifier - - plex_token + - expression + - name PlexSourceRequest: type: object description: Plex Source Serializer @@ -47076,6 +47734,11 @@ components: user_path_template: type: string minLength: 1 + group_matching_mode: + allOf: + - $ref: '#/components/schemas/GroupMatchingModeEnum' + description: How the source determines if an existing group should be used + or a new group created. client_id: type: string minLength: 1 @@ -51639,6 +52302,47 @@ components: readOnly: true required: - paths + UserPlexSourceConnection: + type: object + description: Plex Source connection Serializer + properties: + pk: + type: integer + readOnly: true + title: ID + user: + type: integer + readOnly: true + source: + allOf: + - $ref: '#/components/schemas/Source' + readOnly: true + created: + type: string + format: date-time + readOnly: true + identifier: + type: string + required: + - created + - identifier + - pk + - source + - user + UserPlexSourceConnectionRequest: + type: object + description: Plex Source connection Serializer + properties: + identifier: + type: string + minLength: 1 + plex_token: + type: string + writeOnly: true + minLength: 1 + required: + - identifier + - plex_token UserRequest: type: object description: User Serializer diff --git a/web/src/admin/property-mappings/PropertyMappingListPage.ts b/web/src/admin/property-mappings/PropertyMappingListPage.ts index 5d4b32450c..3a55062ef8 100644 --- a/web/src/admin/property-mappings/PropertyMappingListPage.ts +++ b/web/src/admin/property-mappings/PropertyMappingListPage.ts @@ -3,6 +3,7 @@ import "@goauthentik/admin/property-mappings/PropertyMappingLDAPSourceForm"; import "@goauthentik/admin/property-mappings/PropertyMappingMicrosoftEntraForm"; import "@goauthentik/admin/property-mappings/PropertyMappingNotification"; import "@goauthentik/admin/property-mappings/PropertyMappingOAuthSourceForm"; +import "@goauthentik/admin/property-mappings/PropertyMappingPlexSourceForm"; import "@goauthentik/admin/property-mappings/PropertyMappingRACForm"; import "@goauthentik/admin/property-mappings/PropertyMappingRadiusForm"; import "@goauthentik/admin/property-mappings/PropertyMappingSAMLForm"; diff --git a/web/src/admin/property-mappings/PropertyMappingPlexSourceForm.ts b/web/src/admin/property-mappings/PropertyMappingPlexSourceForm.ts new file mode 100644 index 0000000000..e19277858b --- /dev/null +++ b/web/src/admin/property-mappings/PropertyMappingPlexSourceForm.ts @@ -0,0 +1,40 @@ +import { BasePropertyMappingForm } from "@goauthentik/admin/property-mappings/BasePropertyMappingForm"; +import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; +import "@goauthentik/elements/CodeMirror"; +import "@goauthentik/elements/forms/HorizontalFormElement"; + +import { customElement } from "lit/decorators.js"; + +import { PlexSourcePropertyMapping, PropertymappingsApi } from "@goauthentik/api"; + +@customElement("ak-property-mapping-plex-source-form") +export class PropertyMappingPlexSourceForm extends BasePropertyMappingForm { + docLink(): string { + return "/docs/sources/property-mappings/expression?utm_source=authentik"; + } + + loadInstance(pk: string): Promise { + return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSourcePlexRetrieve({ + pmUuid: pk, + }); + } + + async send(data: PlexSourcePropertyMapping): Promise { + if (this.instance) { + return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSourcePlexUpdate({ + pmUuid: this.instance.pk, + plexSourcePropertyMappingRequest: data, + }); + } else { + return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsSourcePlexCreate({ + plexSourcePropertyMappingRequest: data, + }); + } + } +} + +declare global { + interface HTMLElementTagNameMap { + "ak-property-mapping-plex-source-form": PropertyMappingPlexSourceForm; + } +} diff --git a/web/src/admin/property-mappings/PropertyMappingWizard.ts b/web/src/admin/property-mappings/PropertyMappingWizard.ts index c2ec879367..ea236fc0c0 100644 --- a/web/src/admin/property-mappings/PropertyMappingWizard.ts +++ b/web/src/admin/property-mappings/PropertyMappingWizard.ts @@ -1,6 +1,7 @@ import "@goauthentik/admin/property-mappings/PropertyMappingLDAPSourceForm"; import "@goauthentik/admin/property-mappings/PropertyMappingNotification"; import "@goauthentik/admin/property-mappings/PropertyMappingOAuthSourceForm"; +import "@goauthentik/admin/property-mappings/PropertyMappingPlexSourceForm"; import "@goauthentik/admin/property-mappings/PropertyMappingRACForm"; import "@goauthentik/admin/property-mappings/PropertyMappingSAMLForm"; import "@goauthentik/admin/property-mappings/PropertyMappingSAMLSourceForm"; diff --git a/web/src/admin/sources/plex/PlexSourceForm.ts b/web/src/admin/sources/plex/PlexSourceForm.ts index 3e4c1374a4..530c73fb62 100644 --- a/web/src/admin/sources/plex/PlexSourceForm.ts +++ b/web/src/admin/sources/plex/PlexSourceForm.ts @@ -1,7 +1,10 @@ import "@goauthentik/admin/common/ak-flow-search/ak-source-flow-search"; import { iconHelperText, placeholderHelperText } from "@goauthentik/admin/helperText"; import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm"; -import { UserMatchingModeToLabel } from "@goauthentik/admin/sources/oauth/utils"; +import { + GroupMatchingModeToLabel, + UserMatchingModeToLabel, +} from "@goauthentik/admin/sources/oauth/utils"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { PlexAPIClient, PlexResource, popupCenterScreen } from "@goauthentik/common/helpers/plex"; import { ascii_letters, digits, first, randomString } from "@goauthentik/common/utils"; @@ -9,6 +12,8 @@ import { CapabilitiesEnum, WithCapabilitiesConfig, } from "@goauthentik/elements/Interface/capabilitiesProvider"; +import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js"; +import { DualSelectPair } from "@goauthentik/elements/ak-dual-select/types.js"; import "@goauthentik/elements/forms/FormGroup"; import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/SearchSelect"; @@ -20,11 +25,36 @@ import { ifDefined } from "lit/directives/if-defined.js"; import { FlowsInstancesListDesignationEnum, + GroupMatchingModeEnum, PlexSource, + PlexSourcePropertyMapping, + PropertymappingsApi, SourcesApi, UserMatchingModeEnum, } from "@goauthentik/api"; +async function propertyMappingsProvider(page = 1, search = "") { + const propertyMappings = await new PropertymappingsApi( + DEFAULT_CONFIG, + ).propertymappingsSourcePlexList({ + ordering: "managed", + pageSize: 20, + search: search.trim(), + page, + }); + return { + pagination: propertyMappings.pagination, + options: propertyMappings.results.map((m) => [m.pk, m.name, m.name, m]), + }; +} + +function makePropertyMappingsSelector(instanceMappings?: string[]) { + const localMappings = instanceMappings ? new Set(instanceMappings) : undefined; + return localMappings + ? ([pk, _]: DualSelectPair) => localMappings.has(pk) + : ([_0, _1, _2, _]: DualSelectPair) => false; +} + @customElement("ak-source-plex-form") export class PlexSourceForm extends WithCapabilitiesConfig(BaseSourceForm) { async loadInstance(pk: string): Promise { @@ -245,6 +275,35 @@ export class PlexSourceForm extends WithCapabilitiesConfig(BaseSourceForm + + + + + + ${msg("Plex Attribute mapping")} +
+ + +

+ ${msg("Property mappings for user creation.")} +

+
+ + +

+ ${msg("Property mappings for group creation.")} +

+
+
`; } }