User role access.

This commit is contained in:
charles-gauthereau
2024-11-23 16:05:00 +01:00
parent 9b38d68d51
commit e3fe6d1c80
10 changed files with 144 additions and 56 deletions
@@ -0,0 +1,12 @@
/*
Warnings:
- Added the required column `role` to the `users` table without a default value. This is not possible if the table is not empty.
*/
-- CreateEnum
CREATE TYPE "Role" AS ENUM ('pending', 'user', 'admin');
-- AlterTable
ALTER TABLE "users" DROP COLUMN "role",
ADD COLUMN "role" "Role" NOT NULL;
+7 -1
View File
@@ -59,7 +59,7 @@ model User {
email String? @unique
emailVerified DateTime? @map("email_verified")
image String?
role String?
role Role
password String?
authMethod String? @map("auth_method")
createdAt DateTime @default(now()) @map("created_at")
@@ -71,6 +71,12 @@ model User {
@@map("users")
}
enum Role {
pending
user
admin
}
model Agent {
id String @id @default(cuid())
slug String @unique
+41 -21
View File
@@ -5,9 +5,7 @@ import Credentials from "next-auth/providers/credentials";
import {env} from "@/env.mjs";
import GoogleProvider from "next-auth/providers/google";
class InvalidLoginError extends CredentialsSignin {
code = "Invalid identifier or password"
}
export const {handlers, auth: baseAuth, signIn, signOut} = NextAuth({
adapter: PrismaAdapter(prisma),
@@ -32,7 +30,7 @@ export const {handlers, auth: baseAuth, signIn, signOut} = NextAuth({
email: credentials.email,
}
})
if (!user) {
if (!user || user.role === "pending") {
return null
}
const isValid = await argon2.verify(user.password, credentials.password)
@@ -46,12 +44,22 @@ export const {handlers, auth: baseAuth, signIn, signOut} = NextAuth({
clientId: env.AUTH_GOOGLE_ID,
clientSecret: env.AUTH_GOOGLE_SECRET,
allowDangerousEmailAccountLinking: true,
profile(profile) {
async profile(profile) {
const users = await prisma.user.findMany({
where: {
deleted: {not: true},
}
})
const role = users.length > 0 ? "pending" : "admin"
return {
// role: profile.email === "soluce.technologies@gmail.com" ? "admin" : "user",
role: role,
name: profile.name,
email: profile.email,
image: profile.picture,
email_verified: profile.email_verified,
authMethod: "google",
}
},
@@ -81,26 +89,38 @@ export const {handlers, auth: baseAuth, signIn, signOut} = NextAuth({
session.user = token;
return session;
},
async signIn({ account, user }) {
console.log(account)
// const authMethod = account.provider === 'credentials' ? 'credentials' : 'oauth';
user = await prisma.user.findFirst({
where: {
email: user.email,
}
})
if (!user) {
return true
async signIn({ account, user, profile }) {
const existingUser = await prisma.user.findFirst({
where: { email: user.email },
});
if (!existingUser) {
// Create a new user with a pending role
await prisma.user.create({
data: {
email: user.email,
name: user.name,
image: user.image,
role: "pending",
authMethod: account.provider,
},
});
return false; // Prevent login if role is pending
}
console.log(user)
// Update the user in the database with the auth method
if (existingUser.role === "pending") {
return false; // Prevent login if role is pending
}
// Update auth method if user exists
await prisma.user.update({
where: { id: user.id },
where: { id: existingUser.id },
data: {
authMethod: account.provider
authMethod: account.provider,
},
});
return true;
return true; // Allow login
},
}
@@ -11,11 +11,20 @@ export const registerUserAction = action
const user = await prisma.user.findUnique({ where: { email: parsedInput.email } });
console.log(user);
if (!user && parsedInput.password === parsedInput.confirmPassword) {
const users = await prisma.user.findMany({
where:{
deleted: { not: true },
}
})
const role = users.length > 0 ? "pending" : "admin"
const new_user = await prisma.user.create({
data: {
name: parsedInput.name,
email: parsedInput.email,
password: await hashPassword(parsedInput.password),
role: role
},
});
return {
@@ -1,8 +1,9 @@
import {z} from "zod";
export const UserSchema = z.object({
name: z.string(),
email: z.string(),
name: z.string().optional(),
email: z.string().optional(),
role: z.string().optional(),
});
export type UserType = z.infer<typeof UserSchema>;
@@ -1,7 +1,7 @@
"use client"
import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs";
import {DataTableWithPagination} from "@/components/wrappers/table/data-table-with-pagination";
import {usersColumns} from "@/components/wrappers/Dashboard/Settings/SettingsTabs/columns-users";
import {usersColumns} from "@/components/wrappers/Dashboard/Settings/SettingsUsersTab/columns-users";
import {User, Settings} from "@prisma/client";
import {backupColumns} from "@/features/backup/columns";
import {SettingsEmailTab} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/SettingsEmailTab";
@@ -1,30 +0,0 @@
"use client"
import {ColumnDef} from "@tanstack/react-table"
import {Badge} from "@/components/ui/badge";
import {User} from "@prisma/client";
export const usersColumns: ColumnDef<User>[] = [
{
accessorKey: "name",
header: "Name"
},
{
accessorKey: "email",
header: "Email"
},
{
accessorKey: "createdAt",
header: "Created At",
cell: ({row}) => {
return new Date(row.getValue("createdAt")).toLocaleString("fr-FR");
},
},
{
accessorKey: "authMethod",
header: "Method",
cell: ({row}) => {
return <Badge variant="outline">{row.getValue("authMethod")}</Badge>
},
}
]
@@ -1,5 +1,5 @@
import {DataTableWithPagination} from "@/components/wrappers/table/data-table-with-pagination";
import {usersColumns} from "@/components/wrappers/Dashboard/Settings/SettingsTabs/columns-users";
import {usersColumns} from "@/components/wrappers/Dashboard/Settings/SettingsUsersTab/columns-users";
import {User} from "@prisma/client";
@@ -0,0 +1,68 @@
"use client"
import {ColumnDef} from "@tanstack/react-table"
import {Badge} from "@/components/ui/badge";
import {User} from "@prisma/client";
import {updateUserAction} from "@/components/wrappers/Dashboard/Profile/UserForm/user-form.action";
import {useMutation} from "@tanstack/react-query";
import {toast} from "sonner";
import {useRouter} from "next/navigation";
import {useState} from "react";
export const usersColumns: ColumnDef<User>[] = [
{
accessorKey: "role",
header: "Role",
cell: ({row}) => {
const router = useRouter();
const [role, setRole] = useState<string>(row.getValue("role"))
const updateMutation = useMutation({
mutationFn: () => updateUserAction({id: row.original.id, data: {role: role}}),
onSuccess: () => {
toast.success(`User updated successfully.`);
router.refresh()
},
onError: () => {
toast.error(`An error occurred while updating user information.`);
},
});
const handleUpdateRole = async () => {
const nextRole = role === "admin" ? "pending"
: role === "pending" ? "user"
: "admin";
setRole(nextRole);
await updateMutation.mutateAsync()
};
return <Badge
className="cursor-pointer"
onClick={() => handleUpdateRole()}
variant="outline">{role}</Badge>
},
},
{
accessorKey: "name",
header: "Name"
},
{
accessorKey: "email",
header: "Email"
},
{
accessorKey: "createdAt",
header: "Created At",
cell: ({row}) => {
return new Date(row.getValue("createdAt")).toLocaleString("fr-FR");
},
},
{
accessorKey: "authMethod",
header: "Method",
cell: ({row}) => {
return <Badge variant="outline">{row.getValue("authMethod")}</Badge>
},
}
]