Example #1
0
def to_stage_response(request: HttpRequest,
                      source: HttpResponse) -> HttpResponse:
    """Convert normal HttpResponse into JSON Response"""
    if isinstance(source, HttpResponseRedirect) or source.status_code == 302:
        redirect_url = source["Location"]
        # Redirects to the same URL usually indicate an Error within a form
        if request.get_full_path() == redirect_url:
            return source
        LOGGER.debug(
            "converting to redirect challenge",
            to=str(redirect_url),
            current=request.path,
        )
        return HttpChallengeResponse(
            RedirectChallenge({
                "type": ChallengeTypes.REDIRECT,
                "to": str(redirect_url),
            }))
    if isinstance(source, TemplateResponse):
        return HttpChallengeResponse(
            ShellChallenge({
                "type": ChallengeTypes.SHELL,
                "body": source.render().content.decode("utf-8"),
            }))
    # Check for actual HttpResponse (without isinstance as we don't want to check inheritance)
    if source.__class__ == HttpResponse:
        return HttpChallengeResponse(
            ShellChallenge({
                "type": ChallengeTypes.SHELL,
                "body": source.content.decode("utf-8"),
            }))
    return source
Example #2
0
 def login_challenge(self, source: OAuthSource, request: HttpRequest) -> Challenge:
     """Allow types to return custom challenges"""
     return RedirectChallenge(
         instance={
             "type": ChallengeTypes.REDIRECT.value,
             "to": reverse(
                 "authentik_sources_oauth:oauth-client-login",
                 kwargs={"source_slug": source.slug},
             ),
         }
     )
Example #3
0
 def ui_login_button(self, request: HttpRequest) -> UILoginButton:
     return UILoginButton(
         challenge=RedirectChallenge(
             instance={
                 "type":
                 ChallengeTypes.REDIRECT.value,
                 "to":
                 reverse(
                     "authentik_sources_saml:login",
                     kwargs={"source_slug": self.slug},
                 ),
             }),
         name=self.name,
     )
Example #4
0
 def ui_login_button(self) -> UILoginButton:
     return UILoginButton(
         challenge=RedirectChallenge(
             instance={
                 "type": ChallengeTypes.REDIRECT.value,
                 "to": reverse(
                     "authentik_sources_oauth:oauth-client-login",
                     kwargs={"source_slug": self.slug},
                 ),
             }
         ),
         icon_url=static(f"authentik/sources/{self.provider_type}.svg"),
         name=self.name,
     )
Example #5
0
class PlexSourceViewSet(ModelViewSet):
    """Plex source Viewset"""

    queryset = PlexSource.objects.all()
    serializer_class = PlexSourceSerializer
    lookup_field = "slug"

    @permission_required(None)
    @swagger_auto_schema(
        request_body=PlexTokenRedeemSerializer(),
        responses={
            200: RedirectChallenge(),
            400: "Token not found",
            403: "Access denied",
        },
        manual_parameters=[
            openapi.Parameter(
                name="slug",
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_STRING,
            )
        ],
    )
    @action(
        methods=["POST"],
        detail=False,
        pagination_class=None,
        filter_backends=[],
        permission_classes=[AllowAny],
    )
    def redeem_token(self, request: Request) -> Response:
        """Redeem a plex token, check it's access to resources against what's allowed
        for the source, and redirect to an authentication/enrollment flow."""
        source: PlexSource = get_object_or_404(PlexSource,
                                               slug=request.query_params.get(
                                                   "slug", ""))
        plex_token = request.data.get("plex_token", None)
        if not plex_token:
            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):
                    friends_allowed = True
                    LOGGER.info(
                        "allowing user for plex because of friend",
                        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,
            )
            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"],
        )
        raise PermissionDenied("Access denied.")
Example #6
0
from authentik.flows.stage import PLAN_CONTEXT_PENDING_USER_IDENTIFIER, ChallengeStageView
from authentik.flows.views.executor import SESSION_KEY_APPLICATION_PRE
from authentik.sources.oauth.types.apple import AppleLoginChallenge
from authentik.sources.plex.models import PlexAuthenticationChallenge
from authentik.stages.identification.models import IdentificationStage
from authentik.stages.identification.signals import identification_failed
from authentik.stages.password.stage import authenticate

LOGGER = get_logger()


@extend_schema_field(
    PolymorphicProxySerializer(
        component_name="LoginChallengeTypes",
        serializers={
            RedirectChallenge().fields["component"].default: RedirectChallenge,
            PlexAuthenticationChallenge().fields["component"].default:
            PlexAuthenticationChallenge,
            AppleLoginChallenge().fields["component"].default:
            AppleLoginChallenge,
        },
        resource_type_field_name="component",
    ))
class ChallengeDictWrapper(DictField):
    """Wrapper around DictField that annotates itself as challenge proxy"""


class LoginSourceSerializer(PassiveSerializer):
    """Serializer for Login buttons of sources"""

    name = CharField()
Example #7
0
class PlexSourceViewSet(UsedByMixin, ModelViewSet):
    """Plex source Viewset"""

    queryset = PlexSource.objects.all()
    serializer_class = PlexSourceSerializer
    lookup_field = "slug"
    filterset_fields = [
        "name",
        "slug",
        "enabled",
        "authentication_flow",
        "enrollment_flow",
        "policy_engine_mode",
        "user_matching_mode",
        "client_id",
        "allow_friends",
    ]
    ordering = ["name"]

    @permission_required(None)
    @extend_schema(
        request=PlexTokenRedeemSerializer(),
        responses={
            200: RedirectChallenge(),
            400: OpenApiResponse(description="Token not found"),
            403: OpenApiResponse(description="Access denied"),
        },
        parameters=[
            OpenApiParameter(
                name="slug",
                location=OpenApiParameter.QUERY,
                type=OpenApiTypes.STR,
            )
        ],
    )
    @action(
        methods=["POST"],
        detail=False,
        pagination_class=None,
        filter_backends=[],
        permission_classes=[AllowAny],
    )
    def redeem_token(self, request: Request) -> Response:
        """Redeem a plex token, check it's access to resources against what's allowed
        for the source, and redirect to an authentication/enrollment flow."""
        source: PlexSource = get_object_or_404(PlexSource,
                                               slug=request.query_params.get(
                                                   "slug", ""))
        plex_token = request.data.get("plex_token", None)
        if not plex_token:
            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
        if source.allow_friends:
            owner_api = PlexAuth(source, source.plex_token)
            friends_allowed = owner_api.check_friends_overlap(identifier)
        servers_allowed = auth_api.check_server_overlap()
        if any([friends_allowed, servers_allowed]):
            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"],
        )
        raise PermissionDenied("Access denied.")

    @extend_schema(
        request=PlexTokenRedeemSerializer(),
        responses={
            204: OpenApiResponse(),
            400: OpenApiResponse(description="Token not found"),
            403: OpenApiResponse(description="Access denied"),
        },
        parameters=[
            OpenApiParameter(
                name="slug",
                location=OpenApiParameter.QUERY,
                type=OpenApiTypes.STR,
            )
        ],
    )
    @action(
        methods=["POST"],
        detail=False,
        pagination_class=None,
        filter_backends=[],
        permission_classes=[IsAuthenticated],
    )
    def redeem_token_authenticated(self, request: Request) -> Response:
        """Redeem a plex token for an authenticated user, creating a connection"""
        source: PlexSource = get_object_or_404(PlexSource,
                                               slug=request.query_params.get(
                                                   "slug", ""))
        plex_token = request.data.get("plex_token", None)
        if not plex_token:
            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
        if source.allow_friends:
            owner_api = PlexAuth(source, source.plex_token)
            friends_allowed = owner_api.check_friends_overlap(identifier)
        servers_allowed = auth_api.check_server_overlap()
        if any([friends_allowed, servers_allowed]):
            PlexSourceConnection.objects.create(
                plex_token=plex_token,
                user=request.user,
                identifier=identifier,
                source=source,
            )
            return Response(status=204)
        LOGGER.warning(
            "Denying plex connection because no server overlay and no friends and not owner",
            user=user_info["username"],
            friends_allowed=friends_allowed,
            servers_allowed=servers_allowed,
        )
        raise PermissionDenied("Access denied.")