fix: add authorization to actions for Profile (#908)

* feat: authzn and limit sensitive info from profile actions

* fix: work on suggested changes
This commit is contained in:
Shubham Palriwala
2023-10-02 19:10:15 +05:30
committed by GitHub
parent bcaf2337c4
commit a9c8e99cd4
9 changed files with 34 additions and 19 deletions

View File

@@ -3,14 +3,13 @@
import DeleteDialog from "@/components/shared/DeleteDialog";
import AvatarPlaceholder from "@/images/avatar-placeholder.png";
import { formbricksLogout } from "@/lib/formbricks";
import { TProfile } from "@formbricks/types/v1/profile";
import { Button, Input, ProfileAvatar } from "@formbricks/ui";
import { Session } from "next-auth";
import { signOut } from "next-auth/react";
import Image from "next/image";
import { Dispatch, SetStateAction, useState } from "react";
import toast from "react-hot-toast";
import { profileDeleteAction } from "./actions";
import { deleteProfileAction } from "./actions";
export function EditAvatar({ session }) {
return (
@@ -38,10 +37,9 @@ interface DeleteAccountModalProps {
open: boolean;
setOpen: Dispatch<SetStateAction<boolean>>;
session: Session;
profile: TProfile;
}
function DeleteAccountModal({ setOpen, open, session, profile }: DeleteAccountModalProps) {
function DeleteAccountModal({ setOpen, open, session }: DeleteAccountModalProps) {
const [deleting, setDeleting] = useState(false);
const [inputValue, setInputValue] = useState("");
@@ -52,7 +50,7 @@ function DeleteAccountModal({ setOpen, open, session, profile }: DeleteAccountMo
const deleteAccount = async () => {
try {
setDeleting(true);
await profileDeleteAction(profile.id);
await deleteProfileAction();
await signOut();
await formbricksLogout();
} catch (error) {
@@ -105,7 +103,7 @@ function DeleteAccountModal({ setOpen, open, session, profile }: DeleteAccountMo
);
}
export function DeleteAccount({ session, profile }: { session: Session | null; profile: TProfile }) {
export function DeleteAccount({ session }: { session: Session | null }) {
const [isModalOpen, setModalOpen] = useState(false);
if (!session) {
@@ -114,7 +112,7 @@ export function DeleteAccount({ session, profile }: { session: Session | null; p
return (
<div>
<DeleteAccountModal open={isModalOpen} setOpen={setModalOpen} session={session} profile={profile} />
<DeleteAccountModal open={isModalOpen} setOpen={setModalOpen} session={session} />
<p className="text-sm text-slate-700">
Delete your account with all personal data. <strong>This cannot be undone!</strong>
</p>

View File

@@ -3,7 +3,7 @@
import { Button, Input, Label } from "@formbricks/ui";
import { useForm } from "react-hook-form";
import toast from "react-hot-toast";
import { profileEditAction } from "./actions";
import { updateProfileAction } from "./actions";
import { TProfile } from "@formbricks/types/v1/profile";
export function EditName({ profile }: { profile: TProfile }) {
@@ -19,7 +19,7 @@ export function EditName({ profile }: { profile: TProfile }) {
className="w-full max-w-sm items-center"
onSubmit={handleSubmit(async (data) => {
try {
await profileEditAction(profile.id, data);
await updateProfileAction(data);
toast.success("Your name was updated successfully.");
} catch (error) {
toast.error(`Error: ${error.message}`);

View File

@@ -1,12 +1,21 @@
"use server";
import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
import { updateProfile, deleteProfile } from "@formbricks/lib/services/profile";
import { TProfileUpdateInput } from "@formbricks/types/v1/profile";
import { getServerSession } from "next-auth";
import { AuthorizationError } from "@formbricks/types/v1/errors";
export async function profileEditAction(userId: string, data: Partial<TProfileUpdateInput>) {
return await updateProfile(userId, data);
export async function updateProfileAction(data: Partial<TProfileUpdateInput>) {
const session = await getServerSession(authOptions);
if (!session) throw new AuthorizationError("Not authorized");
return await updateProfile(session.user.id, data);
}
export async function profileDeleteAction(userId: string) {
return await deleteProfile(userId);
export async function deleteProfileAction() {
const session = await getServerSession(authOptions);
if (!session) throw new AuthorizationError("Not authorized");
return await deleteProfile(session.user.id);
}

View File

@@ -28,7 +28,7 @@ export default async function ProfileSettingsPage() {
<SettingsCard
title="Delete account"
description="Delete your account with all of your personal information and data.">
<DeleteAccount session={session} profile={profile} />
<DeleteAccount session={session} />
</SettingsCard>
</div>
)}

View File

@@ -1,12 +1,18 @@
"use server";
import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
import { updateProduct } from "@formbricks/lib/services/product";
import { updateProfile } from "@formbricks/lib/services/profile";
import { TProductUpdateInput } from "@formbricks/types/v1/product";
import { TProfileUpdateInput } from "@formbricks/types/v1/profile";
import { getServerSession } from "next-auth";
import { AuthorizationError } from "@formbricks/types/v1/errors";
export async function updateProfileAction(personId: string, updatedProfile: Partial<TProfileUpdateInput>) {
return await updateProfile(personId, updatedProfile);
export async function updateProfileAction(updatedProfile: Partial<TProfileUpdateInput>) {
const session = await getServerSession(authOptions);
if (!session) throw new AuthorizationError("Not authorized");
return await updateProfile(session.user.id, updatedProfile);
}
export async function updateProductAction(productId: string, updatedProduct: Partial<TProductUpdateInput>) {

View File

@@ -42,7 +42,7 @@ const Objective: React.FC<ObjectiveProps> = ({ next, skip, formbricksResponseId,
try {
setIsProfileUpdating(true);
const updatedProfile = { ...profile, objective: selectedObjective.id };
await updateProfileAction(profile.id, updatedProfile);
await updateProfileAction(updatedProfile);
setIsProfileUpdating(false);
} catch (e) {
setIsProfileUpdating(false);

View File

@@ -54,7 +54,7 @@ export default function Onboarding({ session, environmentId, profile, product }:
try {
const updatedProfile = { ...profile, onboardingCompleted: true };
await updateProfileAction(profile.id, updatedProfile);
await updateProfileAction(updatedProfile);
if (environmentId) {
router.push(`/environments/${environmentId}/surveys`);

View File

@@ -40,7 +40,7 @@ const Role: React.FC<RoleProps> = ({ next, skip, setFormbricksResponseId, profil
try {
setIsUpdating(true);
const updatedProfile = { ...profile, role: selectedRole.id };
await updateProfileAction(profile.id, updatedProfile);
await updateProfileAction(updatedProfile);
setIsUpdating(false);
} catch (e) {
setIsUpdating(false);

View File

@@ -116,6 +116,7 @@ export const updateProfile = async (
id: personId,
},
data: data,
select: responseSelection,
});
revalidateTag(getProfileByEmailCacheTag(updatedProfile.email));
@@ -137,6 +138,7 @@ const deleteUser = async (userId: string): Promise<TProfile> => {
where: {
id: userId,
},
select: responseSelection,
});
revalidateTag(getProfileByEmailCacheTag(profile.email));
revalidateTag(getProfileCacheTag(userId));