mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Add unauthorized guard for the admin panel
This commit is contained in:
parent
81131edf2d
commit
1fb3088f0e
23
front/apps/mobile/app/(app)/admin/index.tsx
Normal file
23
front/apps/mobile/app/(app)/admin/index.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { AdminPage } from "@kyoo/ui";
|
||||
|
||||
export default AdminPage;
|
@ -24,7 +24,8 @@ import { StatusBar, StatusBarProps } from "react-native";
|
||||
import * as ScreenOrientation from "expo-screen-orientation";
|
||||
import * as NavigationBar from "expo-navigation-bar";
|
||||
import arrayShuffle from "array-shuffle";
|
||||
import { QueryPage } from "@kyoo/models";
|
||||
import { QueryPage, useHasPermission } from "@kyoo/models";
|
||||
import { Unauthorized } from "@kyoo/ui";
|
||||
|
||||
const FullscreenProvider = () => {
|
||||
useEffect(() => {
|
||||
@ -49,6 +50,11 @@ export const withRoute = <Props,>(
|
||||
const { statusBar, fullscreen, ...routeOptions } = options ?? {};
|
||||
const WithUseRoute = (props: any) => {
|
||||
const routeParams = useLocalSearchParams();
|
||||
const hasPermissions = useHasPermission((Component as QueryPage).requiredPermissions ?? []);
|
||||
|
||||
if (!hasPermissions)
|
||||
return <Unauthorized missing={(Component as QueryPage).requiredPermissions!} />;
|
||||
|
||||
return (
|
||||
<>
|
||||
{routeOptions && <Stack.Screen {...routeOptions} />}
|
||||
|
24
front/apps/web/src/pages/admin/index.tsx
Normal file
24
front/apps/web/src/pages/admin/index.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { AdminPage } from "@kyoo/ui";
|
||||
import { withRoute } from "~/router";
|
||||
|
||||
export default withRoute(AdminPage);
|
@ -18,6 +18,8 @@
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { QueryPage, useHasPermission } from "@kyoo/models";
|
||||
import { Unauthorized } from "@kyoo/ui";
|
||||
import { useRouter } from "next/router";
|
||||
import { ComponentType } from "react";
|
||||
|
||||
@ -27,7 +29,10 @@ export const withRoute = <Props,>(
|
||||
) => {
|
||||
const WithUseRoute = (props: Props) => {
|
||||
const router = useRouter();
|
||||
const hasPermissions = useHasPermission((Component as QueryPage).requiredPermissions ?? []);
|
||||
|
||||
if (!hasPermissions)
|
||||
return <Unauthorized missing={(Component as QueryPage).requiredPermissions!} />;
|
||||
// @ts-ignore
|
||||
return <Component {...defaultProps} {...router.query} {...props} />;
|
||||
};
|
||||
|
@ -158,9 +158,11 @@ export const useAccounts = () => {
|
||||
return useContext(AccountContext);
|
||||
};
|
||||
|
||||
export const useHasPermission = (...perms: string[]) => {
|
||||
export const useHasPermission = (perms?: string[]) => {
|
||||
const account = useAccount();
|
||||
|
||||
if (!perms) return true;
|
||||
|
||||
// TODO: Read permission of guest account here.
|
||||
if (!account) return false;
|
||||
return perms.every((perm) => account.permissions.includes(perm));
|
||||
|
@ -183,6 +183,7 @@ export type QueryPage<Props = {}, Items = unknown> = ComponentType<
|
||||
getLayout?:
|
||||
| QueryPage<{ page: ReactElement }>
|
||||
| { Layout: QueryPage<{ page: ReactElement }>; props: object };
|
||||
requiredPermissions?: string[]
|
||||
randomItems?: Items[];
|
||||
};
|
||||
|
||||
|
35
front/packages/ui/src/admin/index.tsx
Normal file
35
front/packages/ui/src/admin/index.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { QueryPage } from "@kyoo/models";
|
||||
import { P, ts } from "@kyoo/primitives";
|
||||
import { ScrollView } from "react-native";
|
||||
import { DefaultLayout } from "../layout";
|
||||
|
||||
export const AdminPage: QueryPage = () => {
|
||||
return (
|
||||
<ScrollView contentContainerStyle={{ gap: ts(4), paddingBottom: ts(4) }}>
|
||||
<P>hbgen</P>
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
AdminPage.getLayout = DefaultLayout;
|
||||
AdminPage.requiredPermissions = ["admin.read"];
|
@ -28,7 +28,7 @@ import {
|
||||
import { Container, H2, ImageBackground, Link, P, focusReset, ts } from "@kyoo/primitives";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Theme, useYoshiki } from "yoshiki/native";
|
||||
import { ErrorView } from "../fetch";
|
||||
import { ErrorView } from "../errors";
|
||||
|
||||
export const PartOf = ({
|
||||
name,
|
||||
|
46
front/packages/ui/src/errors/error.tsx
Normal file
46
front/packages/ui/src/errors/error.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { KyooErrors } from "@kyoo/models";
|
||||
import { P } from "@kyoo/primitives";
|
||||
import { View } from "react-native";
|
||||
import { useYoshiki } from "yoshiki/native";
|
||||
|
||||
export const ErrorView = ({ error }: { error: KyooErrors }) => {
|
||||
const { css } = useYoshiki();
|
||||
|
||||
return (
|
||||
<View
|
||||
{...css({
|
||||
backgroundColor: (theme) => theme.colors.red,
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
})}
|
||||
>
|
||||
{error.errors.map((x, i) => (
|
||||
<P key={i} {...css({ color: (theme) => theme.colors.white })}>
|
||||
{x}
|
||||
</P>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
};
|
22
front/packages/ui/src/errors/index.tsx
Normal file
22
front/packages/ui/src/errors/index.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export * from "./error";
|
||||
export * from "./unauthorized";
|
42
front/packages/ui/src/errors/unauthorized.tsx
Normal file
42
front/packages/ui/src/errors/unauthorized.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { P } from "@kyoo/primitives";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import { useYoshiki } from "yoshiki/native";
|
||||
|
||||
export const Unauthorized = ({ missing }: { missing: string[] }) => {
|
||||
const { t } = useTranslation();
|
||||
const { css } = useYoshiki();
|
||||
|
||||
return (
|
||||
<View
|
||||
{...css({
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
})}
|
||||
>
|
||||
<P>{t("errors.unauthorized", { permission: missing.join(", ") })}</P>
|
||||
</View>
|
||||
);
|
||||
};
|
@ -22,7 +22,8 @@ import { Page, QueryIdentifier, useInfiniteFetch } from "@kyoo/models";
|
||||
import { useBreakpointMap, HR } from "@kyoo/primitives";
|
||||
import { ContentStyle, FlashList } from "@shopify/flash-list";
|
||||
import { ComponentProps, ComponentType, isValidElement, ReactElement, useRef } from "react";
|
||||
import { EmptyView, ErrorView, Layout, OfflineView, WithLoading, addHeader } from "./fetch";
|
||||
import { EmptyView, Layout, OfflineView, WithLoading, addHeader } from "./fetch";
|
||||
import { ErrorView } from "./errors";
|
||||
import { FlatList, View, ViewStyle } from "react-native";
|
||||
|
||||
const emulateGap = (
|
||||
|
@ -31,7 +31,8 @@ import {
|
||||
useRef,
|
||||
} from "react";
|
||||
import { Stylable, nativeStyleToCss, useYoshiki, ysMap } from "yoshiki";
|
||||
import { EmptyView, ErrorView, Layout, WithLoading, addHeader } from "./fetch";
|
||||
import { EmptyView, Layout, WithLoading, addHeader } from "./fetch";
|
||||
import { ErrorView } from "./errors";
|
||||
import type { ContentStyle } from "@shopify/flash-list";
|
||||
|
||||
const InfiniteScroll = <Props,>({
|
||||
|
@ -18,12 +18,13 @@
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Page, QueryIdentifier, useFetch, KyooErrors } from "@kyoo/models";
|
||||
import { Page, QueryIdentifier, useFetch } from "@kyoo/models";
|
||||
import { Breakpoint, P } from "@kyoo/primitives";
|
||||
import { ComponentType, ReactElement, isValidElement } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import { useYoshiki } from "yoshiki/native";
|
||||
import { ErrorView } from "./errors";
|
||||
|
||||
export type Layout = {
|
||||
numColumns: Breakpoint<number>;
|
||||
@ -84,28 +85,6 @@ export const OfflineView = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const ErrorView = ({ error }: { error: KyooErrors }) => {
|
||||
const { css } = useYoshiki();
|
||||
|
||||
return (
|
||||
<View
|
||||
{...css({
|
||||
backgroundColor: (theme) => theme.colors.red,
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
})}
|
||||
>
|
||||
{error.errors.map((x, i) => (
|
||||
<P key={i} {...css({ color: (theme) => theme.colors.white })}>
|
||||
{x}
|
||||
</P>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export const EmptyView = ({ message }: { message: string }) => {
|
||||
const { css } = useYoshiki();
|
||||
|
||||
|
@ -28,3 +28,5 @@ export { SearchPage } from "./search";
|
||||
export { LoginPage, RegisterPage } from "./login";
|
||||
export { DownloadPage, DownloadProvider } from "./downloads";
|
||||
export { SettingsPage } from "./settings";
|
||||
export { AdminPage } from "./admin";
|
||||
export * from "./errors";
|
||||
|
@ -44,6 +44,7 @@ import Admin from "@material-symbols/svg-400/rounded/admin_panel_settings.svg";
|
||||
import Settings from "@material-symbols/svg-400/rounded/settings.svg";
|
||||
import { KyooLongLogo } from "./icon";
|
||||
import { forwardRef, useEffect, useRef, useState } from "react";
|
||||
import { AdminPage } from "../admin";
|
||||
|
||||
export const NavbarTitle = (props: Stylable & { onLayout?: ViewProps["onLayout"] }) => {
|
||||
const { t } = useTranslation();
|
||||
@ -139,7 +140,7 @@ export const NavbarRight = () => {
|
||||
const { css, theme } = useYoshiki();
|
||||
const { t } = useTranslation();
|
||||
const { push } = useRouter();
|
||||
const isAdmin = useHasPermission("admin.read");
|
||||
const isAdmin = useHasPermission(AdminPage.requiredPermissions);
|
||||
|
||||
return (
|
||||
<View {...css({ flexDirection: "row", alignItems: "center", flexShrink: 1 })}>
|
||||
|
@ -22,12 +22,12 @@ import { useFetch, QueryIdentifier, imageFn, Chapter } from "@kyoo/models";
|
||||
import { Sprite, P, imageBorderRadius, ts } from "@kyoo/primitives";
|
||||
import { View, Platform } from "react-native";
|
||||
import { percent, useYoshiki, px, Theme, useForceRerender } from "yoshiki/native";
|
||||
import { ErrorView } from "../../fetch";
|
||||
import { useMemo } from "react";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { durationAtom } from "../state";
|
||||
import { toTimerString } from "./left-buttons";
|
||||
import { seekProgressAtom } from "./hover";
|
||||
import { ErrorView } from "../../errors";
|
||||
|
||||
type Thumb = {
|
||||
from: number;
|
||||
|
@ -40,8 +40,8 @@ import { fullscreenAtom, Video } from "./state";
|
||||
import { episodeDisplayNumber } from "../details/episode";
|
||||
import { useVideoKeyboard } from "./keyboard";
|
||||
import { MediaSessionManager } from "./media-session";
|
||||
import { ErrorView } from "../fetch";
|
||||
import { WatchStatusObserver } from "./watch-status-observer";
|
||||
import { ErrorView } from "../errors";
|
||||
|
||||
type Item = (Movie & { type: "movie" }) | (Episode & { type: "episode" });
|
||||
|
||||
@ -66,8 +66,6 @@ const mapData = (
|
||||
};
|
||||
};
|
||||
|
||||
let firstSlug: string | null = null;
|
||||
|
||||
export const Player = ({
|
||||
slug,
|
||||
type,
|
||||
|
@ -189,11 +189,12 @@
|
||||
},
|
||||
"errors": {
|
||||
"connection": "Could not connect to the kyoo's server",
|
||||
"connection-tips": "Troublshotting tips:\n - Are you connected to internet?\n - Is your kyoo's server online?\n - Have your account been banned?",
|
||||
"connection-tips": "Troubleshooting tips:\n - Are you connected to internet?\n - Is your kyoo's server online?\n - Have your account been banned?",
|
||||
"unknown": "Unknown error",
|
||||
"try-again": "Try again",
|
||||
"re-login": "Re login",
|
||||
"offline": "You are not connected to internet. Try again later."
|
||||
"offline": "You are not connected to internet. Try again later.",
|
||||
"unauthorized": "You are missing the permissions {{permission}} to access this page."
|
||||
},
|
||||
"mediainfo": {
|
||||
"file": "File",
|
||||
|
@ -193,7 +193,8 @@
|
||||
"unknown": "Erreur inconnue",
|
||||
"try-again": "Réessayer",
|
||||
"re-login": "Se reconnecter",
|
||||
"offline": "Vous n'êtes pas connecté à Internet. Réessayez plus tard."
|
||||
"offline": "Vous n'êtes pas connecté à Internet. Réessayez plus tard.",
|
||||
"unauthorized": "Il vous manque les autorisations {{permission}} pour accéder à cette page."
|
||||
},
|
||||
"mediainfo": {
|
||||
"file": "Fichier",
|
||||
|
Loading…
x
Reference in New Issue
Block a user