From 6526659b51c8da5dc6a5cc6dd5d087813b574d95 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Thu, 6 May 2021 21:50:15 +0200 Subject: [PATCH] sources/plex: allow auth for owner (when identifier of source plex token matches) Signed-off-by: Jens Langhammer --- authentik/core/sources/flow_manager.py | 1 + authentik/sources/plex/api.py | 37 +++++++++++++-------- authentik/sources/plex/plex.py | 4 +-- web/src/flows/sources/plex/PlexLoginInit.ts | 14 +++++++- 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/authentik/core/sources/flow_manager.py b/authentik/core/sources/flow_manager.py index b718fe30a..7be2e6d6e 100644 --- a/authentik/core/sources/flow_manager.py +++ b/authentik/core/sources/flow_manager.py @@ -149,6 +149,7 @@ class SourceFlowManager: def get_flow(self, **kwargs) -> HttpResponse: """Get the flow response based on user_matching_mode""" action, connection = self.get_action() + self._logger.debug("get_action() says", action=action, connection=connection) if connection: if action == Action.LINK: self._logger.debug("Linking existing user") diff --git a/authentik/sources/plex/api.py b/authentik/sources/plex/api.py index 6b9107338..38a35c994 100644 --- a/authentik/sources/plex/api.py +++ b/authentik/sources/plex/api.py @@ -4,10 +4,12 @@ from django.shortcuts import get_object_or_404 from drf_yasg import openapi from drf_yasg.utils import swagger_auto_schema from rest_framework.decorators import action +from rest_framework.exceptions import PermissionDenied from rest_framework.fields import CharField from rest_framework.permissions import AllowAny from rest_framework.request import Request from rest_framework.response import Response +from rest_framework.serializers import ValidationError from rest_framework.viewsets import ModelViewSet from structlog.stdlib import get_logger @@ -15,6 +17,7 @@ from authentik.api.decorators import permission_required from authentik.core.api.sources import SourceSerializer from authentik.core.api.utils import PassiveSerializer from authentik.flows.challenge import RedirectChallenge +from authentik.flows.views import to_stage_response from authentik.sources.plex.models import PlexSource from authentik.sources.plex.plex import PlexAuth, PlexSourceFlowManager @@ -51,7 +54,11 @@ class PlexSourceViewSet(ModelViewSet): @permission_required(None) @swagger_auto_schema( request_body=PlexTokenRedeemSerializer(), - responses={200: RedirectChallenge(), 404: "Token not found"}, + responses={ + 200: RedirectChallenge(), + 400: "Token not found", + 403: "Access denied", + }, manual_parameters=[ openapi.Parameter( name="slug", @@ -75,13 +82,15 @@ class PlexSourceViewSet(ModelViewSet): ) plex_token = request.data.get("plex_token", None) if not plex_token: - raise Http404 + raise ValidationError("No plex token given") auth_api = PlexAuth(source, plex_token) user_info, identifier = auth_api.get_user_info() # Check friendship first, then check server overlay friends_allowed = False + owner_id = None if source.allow_friends: owner_api = PlexAuth(source, source.plex_token) + owner_id = owner_api.get_user_info owner_friends = owner_api.get_friends() for friend in owner_friends: if int(friend.get("id", "0")) == int(identifier): @@ -90,16 +99,18 @@ class PlexSourceViewSet(ModelViewSet): "allowing user for plex because of friend", user=user_info["username"], ) - if not auth_api.check_server_overlap() or not friends_allowed: - LOGGER.warning( - "Denying plex auth because no server overlay and no friends", - user=user_info["username"], + servers_allowed = auth_api.check_server_overlap() + owner_allowed = owner_id == identifier + if any([friends_allowed, servers_allowed, owner_allowed]): + sfm = PlexSourceFlowManager( + source=source, + request=request, + identifier=str(identifier), + enroll_info=user_info, ) - raise Http404 - sfm = PlexSourceFlowManager( - source=source, - request=request, - identifier=str(identifier), - enroll_info=user_info, + return to_stage_response(request, sfm.get_flow(plex_token=plex_token)) + LOGGER.warning( + "Denying plex auth because no server overlay and no friends and not owner", + user=user_info["username"], ) - return sfm.get_flow(plex_token=plex_token) + raise PermissionDenied("Access denied.") diff --git a/authentik/sources/plex/plex.py b/authentik/sources/plex/plex.py index 697d6f909..ecfb7a5a4 100644 --- a/authentik/sources/plex/plex.py +++ b/authentik/sources/plex/plex.py @@ -105,8 +105,8 @@ class PlexSourceFlowManager(SourceFlowManager): connection_type = PlexSourceConnection def update_connection( - self, connection: PlexSourceConnection, plex_token: str + self, connection: PlexSourceConnection, **kwargs ) -> PlexSourceConnection: """Set the access_token on the connection""" - connection.plex_token = plex_token + connection.plex_token = kwargs.get("plex_token") return connection diff --git a/web/src/flows/sources/plex/PlexLoginInit.ts b/web/src/flows/sources/plex/PlexLoginInit.ts index a968f8293..3d9351e81 100644 --- a/web/src/flows/sources/plex/PlexLoginInit.ts +++ b/web/src/flows/sources/plex/PlexLoginInit.ts @@ -13,6 +13,8 @@ import { BaseStage } from "../../stages/base"; import { PlexAPIClient, popupCenterScreen } from "./API"; import { DEFAULT_CONFIG } from "../../../api/Config"; import { SourcesApi } from "authentik-api"; +import { showMessage } from "../../../elements/messages/MessageContainer"; +import { MessageLevel } from "../../../elements/messages/Message"; export interface PlexAuthenticationChallenge extends Challenge { @@ -41,8 +43,18 @@ export class PlexLoginInit extends BaseStage { plexToken: token, }, slug: this.challenge?.slug || "", - }).then(r => { + }).then((r) => { window.location.assign(r.to); + }).catch((r: Response) => { + r.json().then((body: {detail: string}) => { + showMessage({ + level: MessageLevel.error, + message: body.detail + }); + setTimeout(() => { + window.location.assign("/"); + }, 5000); + }); }); }); }