sources/plex: allow auth for owner (when identifier of source plex token matches)

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-05-06 21:50:15 +02:00
parent 6c3b7c8d3e
commit 6526659b51
4 changed files with 40 additions and 16 deletions

View file

@ -149,6 +149,7 @@ class SourceFlowManager:
def get_flow(self, **kwargs) -> HttpResponse: def get_flow(self, **kwargs) -> HttpResponse:
"""Get the flow response based on user_matching_mode""" """Get the flow response based on user_matching_mode"""
action, connection = self.get_action() action, connection = self.get_action()
self._logger.debug("get_action() says", action=action, connection=connection)
if connection: if connection:
if action == Action.LINK: if action == Action.LINK:
self._logger.debug("Linking existing user") self._logger.debug("Linking existing user")

View file

@ -4,10 +4,12 @@ from django.shortcuts import get_object_or_404
from drf_yasg import openapi from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from rest_framework.fields import CharField from rest_framework.fields import CharField
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.serializers import ValidationError
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from structlog.stdlib import get_logger 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.sources import SourceSerializer
from authentik.core.api.utils import PassiveSerializer from authentik.core.api.utils import PassiveSerializer
from authentik.flows.challenge import RedirectChallenge 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.models import PlexSource
from authentik.sources.plex.plex import PlexAuth, PlexSourceFlowManager from authentik.sources.plex.plex import PlexAuth, PlexSourceFlowManager
@ -51,7 +54,11 @@ class PlexSourceViewSet(ModelViewSet):
@permission_required(None) @permission_required(None)
@swagger_auto_schema( @swagger_auto_schema(
request_body=PlexTokenRedeemSerializer(), request_body=PlexTokenRedeemSerializer(),
responses={200: RedirectChallenge(), 404: "Token not found"}, responses={
200: RedirectChallenge(),
400: "Token not found",
403: "Access denied",
},
manual_parameters=[ manual_parameters=[
openapi.Parameter( openapi.Parameter(
name="slug", name="slug",
@ -75,13 +82,15 @@ class PlexSourceViewSet(ModelViewSet):
) )
plex_token = request.data.get("plex_token", None) plex_token = request.data.get("plex_token", None)
if not plex_token: if not plex_token:
raise Http404 raise ValidationError("No plex token given")
auth_api = PlexAuth(source, plex_token) auth_api = PlexAuth(source, plex_token)
user_info, identifier = auth_api.get_user_info() user_info, identifier = auth_api.get_user_info()
# Check friendship first, then check server overlay # Check friendship first, then check server overlay
friends_allowed = False friends_allowed = False
owner_id = None
if source.allow_friends: if source.allow_friends:
owner_api = PlexAuth(source, source.plex_token) owner_api = PlexAuth(source, source.plex_token)
owner_id = owner_api.get_user_info
owner_friends = owner_api.get_friends() owner_friends = owner_api.get_friends()
for friend in owner_friends: for friend in owner_friends:
if int(friend.get("id", "0")) == int(identifier): if int(friend.get("id", "0")) == int(identifier):
@ -90,16 +99,18 @@ class PlexSourceViewSet(ModelViewSet):
"allowing user for plex because of friend", "allowing user for plex because of friend",
user=user_info["username"], user=user_info["username"],
) )
if not auth_api.check_server_overlap() or not friends_allowed: servers_allowed = auth_api.check_server_overlap()
LOGGER.warning( owner_allowed = owner_id == identifier
"Denying plex auth because no server overlay and no friends", if any([friends_allowed, servers_allowed, owner_allowed]):
user=user_info["username"], sfm = PlexSourceFlowManager(
source=source,
request=request,
identifier=str(identifier),
enroll_info=user_info,
) )
raise Http404 return to_stage_response(request, sfm.get_flow(plex_token=plex_token))
sfm = PlexSourceFlowManager( LOGGER.warning(
source=source, "Denying plex auth because no server overlay and no friends and not owner",
request=request, user=user_info["username"],
identifier=str(identifier),
enroll_info=user_info,
) )
return sfm.get_flow(plex_token=plex_token) raise PermissionDenied("Access denied.")

View file

@ -105,8 +105,8 @@ class PlexSourceFlowManager(SourceFlowManager):
connection_type = PlexSourceConnection connection_type = PlexSourceConnection
def update_connection( def update_connection(
self, connection: PlexSourceConnection, plex_token: str self, connection: PlexSourceConnection, **kwargs
) -> PlexSourceConnection: ) -> PlexSourceConnection:
"""Set the access_token on the connection""" """Set the access_token on the connection"""
connection.plex_token = plex_token connection.plex_token = kwargs.get("plex_token")
return connection return connection

View file

@ -13,6 +13,8 @@ import { BaseStage } from "../../stages/base";
import { PlexAPIClient, popupCenterScreen } from "./API"; import { PlexAPIClient, popupCenterScreen } from "./API";
import { DEFAULT_CONFIG } from "../../../api/Config"; import { DEFAULT_CONFIG } from "../../../api/Config";
import { SourcesApi } from "authentik-api"; import { SourcesApi } from "authentik-api";
import { showMessage } from "../../../elements/messages/MessageContainer";
import { MessageLevel } from "../../../elements/messages/Message";
export interface PlexAuthenticationChallenge extends Challenge { export interface PlexAuthenticationChallenge extends Challenge {
@ -41,8 +43,18 @@ export class PlexLoginInit extends BaseStage {
plexToken: token, plexToken: token,
}, },
slug: this.challenge?.slug || "", slug: this.challenge?.slug || "",
}).then(r => { }).then((r) => {
window.location.assign(r.to); 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);
});
}); });
}); });
} }