mirror of
https://github.com/QuivrHQ/quivr.git
synced 2024-10-26 15:18:16 +03:00
feat: prevent editor to share as owner (#715)
* feat(UploadPage): redirect to login if not connected * feat: prevent editor to share as owner
This commit is contained in:
parent
6c5016c79a
commit
d7ca11f5d1
@ -54,6 +54,19 @@ def invite_users_to_brain(
|
||||
subscription = BrainSubscription(
|
||||
brain_id=brain_id, email=user["email"], rights=user["rights"]
|
||||
)
|
||||
# check if user is an editor but trying to give high level permissions
|
||||
if subscription.rights == "Owner":
|
||||
try:
|
||||
validate_brain_authorization(
|
||||
brain_id,
|
||||
current_user.id,
|
||||
RoleEnum.Owner,
|
||||
)
|
||||
except HTTPException:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="You don't have the rights to give owner permissions",
|
||||
)
|
||||
|
||||
try:
|
||||
subscription_service.create_or_update_subscription_invitation(subscription)
|
||||
|
@ -6,6 +6,8 @@ import Button from "@/lib/components/ui/Button";
|
||||
import { Divider } from "@/lib/components/ui/Divider";
|
||||
import PageHeading from "@/lib/components/ui/PageHeading";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
import { useSupabase } from "@/lib/context/SupabaseProvider";
|
||||
import { redirectToLogin } from "@/lib/router/redirectToLogin";
|
||||
|
||||
import { Crawler } from "./components/Crawler";
|
||||
import { FileUploader } from "./components/FileUploader";
|
||||
@ -14,6 +16,11 @@ const requiredRolesForUpload: BrainRoleType[] = ["Editor", "Owner"];
|
||||
|
||||
const UploadPage = (): JSX.Element => {
|
||||
const { currentBrain } = useBrainContext();
|
||||
const { session } = useSupabase();
|
||||
|
||||
if (session === null) {
|
||||
redirectToLogin();
|
||||
}
|
||||
|
||||
if (currentBrain === undefined) {
|
||||
return (
|
||||
|
@ -6,6 +6,10 @@ import {
|
||||
BrainConfigContextMock,
|
||||
BrainConfigProviderMock,
|
||||
} from "@/lib/context/BrainConfigProvider/mocks/BrainConfigProviderMock";
|
||||
import {
|
||||
BrainContextMock,
|
||||
BrainProviderMock,
|
||||
} from "@/lib/context/BrainProvider/mocks/BrainProviderMock";
|
||||
import {
|
||||
SupabaseContextMock,
|
||||
SupabaseProviderMock,
|
||||
@ -21,6 +25,26 @@ vi.mock("@/lib/context/BrainConfigProvider/brain-config-provider", () => ({
|
||||
BrainConfigContext: BrainConfigContextMock,
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/context/BrainProvider/brain-provider", () => ({
|
||||
BrainContext: BrainContextMock,
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/context/BrainProvider/hooks/useBrainContext", async () => {
|
||||
const actual = await vi.importActual<
|
||||
typeof import("@/lib/context/BrainProvider/hooks/useBrainContext")
|
||||
>("@/lib/context/BrainProvider/hooks/useBrainContext");
|
||||
|
||||
return {
|
||||
...actual,
|
||||
useBrainContext: () => ({
|
||||
...actual.useBrainContext(),
|
||||
currentBrain: {
|
||||
rights: "Editor",
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("@/lib/api/brain/useBrainApi", async () => {
|
||||
const actual = await vi.importActual<
|
||||
typeof import("@/lib/api/brain/useBrainApi")
|
||||
@ -44,10 +68,12 @@ describe("ShareBrain", () => {
|
||||
const { getByTestId } = render(
|
||||
<SupabaseProviderMock>
|
||||
<BrainConfigProviderMock>
|
||||
<ShareBrain
|
||||
name="test"
|
||||
brainId="cf9bb422-b1b6-4fd7-abc1-01bd395d2318"
|
||||
/>
|
||||
<BrainProviderMock>
|
||||
<ShareBrain
|
||||
name="test"
|
||||
brainId="cf9bb422-b1b6-4fd7-abc1-01bd395d2318"
|
||||
/>
|
||||
</BrainProviderMock>
|
||||
</BrainConfigProviderMock>
|
||||
</SupabaseProviderMock>
|
||||
);
|
||||
@ -59,12 +85,14 @@ describe("ShareBrain", () => {
|
||||
const { getByText, getByTestId } = render(
|
||||
// Todo: add a custom render function that wraps the component with the providers
|
||||
<SupabaseProviderMock>
|
||||
<BrainConfigProviderMock>
|
||||
<ShareBrain
|
||||
name="test"
|
||||
brainId="cf9bb422-b1b6-4fd7-abc1-01bd395d2318"
|
||||
/>
|
||||
</BrainConfigProviderMock>
|
||||
<BrainProviderMock>
|
||||
<BrainConfigProviderMock>
|
||||
<ShareBrain
|
||||
name="test"
|
||||
brainId="cf9bb422-b1b6-4fd7-abc1-01bd395d2318"
|
||||
/>
|
||||
</BrainConfigProviderMock>
|
||||
</BrainProviderMock>
|
||||
</SupabaseProviderMock>
|
||||
);
|
||||
const shareButton = getByTestId("share-brain-button");
|
||||
@ -76,10 +104,12 @@ describe("ShareBrain", () => {
|
||||
const { getByTestId, findAllByTestId } = render(
|
||||
<SupabaseProviderMock>
|
||||
<BrainConfigProviderMock>
|
||||
<ShareBrain
|
||||
name="test"
|
||||
brainId="cf9bb422-b1b6-4fd7-abc1-01bd395d2318"
|
||||
/>
|
||||
<BrainProviderMock>
|
||||
<ShareBrain
|
||||
name="test"
|
||||
brainId="cf9bb422-b1b6-4fd7-abc1-01bd395d2318"
|
||||
/>
|
||||
</BrainProviderMock>
|
||||
</BrainConfigProviderMock>
|
||||
</SupabaseProviderMock>
|
||||
);
|
||||
|
@ -3,9 +3,10 @@ import { MdOutlineRemoveCircle } from "react-icons/md";
|
||||
|
||||
import Field from "@/lib/components/ui/Field";
|
||||
import { Select } from "@/lib/components/ui/Select";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
|
||||
import { BrainRoleAssignation, BrainRoleType } from "../../../types";
|
||||
import { availableRoles } from "../types";
|
||||
import { userRoleToAssignableRoles } from "../types";
|
||||
|
||||
type UserToInviteProps = {
|
||||
onChange: (newRole: BrainRoleAssignation) => void;
|
||||
@ -22,6 +23,11 @@ export const UserToInvite = ({
|
||||
roleAssignation.rights
|
||||
);
|
||||
const [email, setEmail] = useState(roleAssignation.email);
|
||||
const { currentBrain } = useBrainContext();
|
||||
|
||||
if (currentBrain === undefined) {
|
||||
throw new Error("Brain is undefined");
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
onChange({
|
||||
@ -54,7 +60,7 @@ export const UserToInvite = ({
|
||||
<Select
|
||||
onChange={setSelectedRole}
|
||||
value={selectedRole}
|
||||
options={availableRoles}
|
||||
options={userRoleToAssignableRoles[currentBrain.rights]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* eslint-disable max-lines */
|
||||
import axios, { AxiosResponse } from "axios";
|
||||
import { useState } from "react";
|
||||
|
||||
import { useBrainApi } from "@/lib/api/brain/useBrainApi";
|
||||
@ -77,10 +78,21 @@ export const useShareBrain = (brainId: string) => {
|
||||
setIsShareModalOpen(false);
|
||||
setRoleAssignation([generateBrainAssignation()]);
|
||||
} catch (error) {
|
||||
publish({
|
||||
variant: "danger",
|
||||
text: "An error occurred while sending invitations",
|
||||
});
|
||||
if (axios.isAxiosError(error) && error.response?.data !== undefined) {
|
||||
publish({
|
||||
variant: "danger",
|
||||
text: (
|
||||
error.response as AxiosResponse<{
|
||||
detail: string;
|
||||
}>
|
||||
).data.detail,
|
||||
});
|
||||
} else {
|
||||
publish({
|
||||
variant: "danger",
|
||||
text: "An error occurred while sending invitations",
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
setSendingInvitation(false);
|
||||
}
|
||||
|
@ -9,3 +9,20 @@ export const availableRoles: SelectOptionsProps[] = [
|
||||
{ label: "Editor", value: "Editor" },
|
||||
{ label: "Owner", value: "Owner" },
|
||||
];
|
||||
|
||||
export const userRoleToAssignableRoles: Record<
|
||||
BrainRoleType,
|
||||
SelectOptionsProps[]
|
||||
> = {
|
||||
Viewer: [],
|
||||
Editor: [
|
||||
{ label: "Viewer", value: "Viewer" },
|
||||
{ label: "Editor", value: "Editor" },
|
||||
],
|
||||
|
||||
Owner: [
|
||||
{ label: "Viewer", value: "Viewer" },
|
||||
{ label: "Editor", value: "Editor" },
|
||||
{ label: "Owner", value: "Owner" },
|
||||
],
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user