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:
Mamadou DICKO 2023-07-20 15:15:32 +02:00 committed by GitHub
parent 6c5016c79a
commit d7ca11f5d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 105 additions and 20 deletions

View File

@ -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)

View File

@ -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 (

View File

@ -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>
);

View File

@ -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>
);

View File

@ -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);
}

View File

@ -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" },
],
};