Merge pull request #103 from calcom/feat/admin-users

feat: Admin API users manegement endpoints
pull/9078/head
Agusti Fernandez Pardo 2022-05-31 21:00:51 +02:00 committed by GitHub
commit 767f170cae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 71 additions and 33 deletions

7
lib/utils/isAdmin.ts Normal file
View File

@ -0,0 +1,7 @@
import prisma from "@calcom/prisma";
import { UserPermissionRole } from "@calcom/prisma/client";
export const isAdminGuard = async (userId: number) => {
const user = await prisma.user.findUnique({ where: { id: userId } });
return user?.role === UserPermissionRole.ADMIN;
};

View File

@ -85,19 +85,45 @@ const schemaUserEditParams = z.object({
bufferTime: z.number().min(0).max(86400).optional(),
startTime: z.number().min(0).max(86400).optional(),
endTime: z.number().min(0).max(86400).optional(),
theme: z.nativeEnum(theme).optional(),
theme: z.nativeEnum(theme).optional().nullable(),
timeFormat: z.nativeEnum(timeFormat).optional(),
defaultScheduleId: z
.number()
.refine((id: number) => id > 0)
.optional(),
locale: z.nativeEnum(locales).optional(),
.optional()
.nullable(),
locale: z.nativeEnum(locales).optional().nullable(),
metadata: jsonSchema,
});
// @note: These are the values that are editable via PATCH method on the user Model,
// merging both BaseBodyParams with RequiredParams, and omiting whatever we want at the end.
const schemaUserCreateParams = z.object({
email: z.string().email(),
weekStart: z.nativeEnum(weekdays).optional(),
brandColor: z.string().min(4).max(9).regex(/^#/).optional(),
darkBrandColor: z.string().min(4).max(9).regex(/^#/).optional(),
timeZone: timeZone.optional(),
bufferTime: z.number().min(0).max(86400).optional(),
startTime: z.number().min(0).max(86400).optional(),
endTime: z.number().min(0).max(86400).optional(),
theme: z.nativeEnum(theme).optional().nullable(),
timeFormat: z.nativeEnum(timeFormat).optional(),
defaultScheduleId: z
.number()
.refine((id: number) => id > 0)
.optional()
.nullable(),
locale: z.nativeEnum(locales).optional(),
metadata: jsonSchema,
createdDate: z.string().or(z.date()).optional(),
});
// @note: These are the values that are editable via PATCH method on the user Model,
// merging both BaseBodyParams with RequiredParams, and omiting whatever we want at the end.
export const schemaUserEditBodyParams = schemaUserBaseBodyParams.merge(schemaUserEditParams).omit({});
export const schemaUserCreateBodyParams = schemaUserBaseBodyParams.merge(schemaUserCreateParams).omit({});
// @note: These are the values that are always returned when reading a user
export const schemaUserReadPublic = User.pick({
@ -124,4 +150,4 @@ export const schemaUserReadPublic = User.pick({
createdDate: true,
verified: true,
invitedTo: true,
});
}).merge(schemaUserEditBodyParams);

View File

@ -4,6 +4,7 @@ import prisma from "@calcom/prisma";
import { withMiddleware } from "@lib/helpers/withMiddleware";
import type { UserResponse } from "@lib/types";
import { isAdminGuard } from "@lib/utils/isAdmin";
import {
schemaQueryIdParseInt,
withValidQueryIdTransformParseInt,
@ -20,8 +21,11 @@ export async function userById(
res.status(400).json({ message: "Your query was invalid" });
return;
}
if (safeQuery.data.id !== userId) res.status(401).json({ message: "Unauthorized" });
else {
const isAdmin = await isAdminGuard(userId);
// Here we only check for ownership of the user if the user is not admin, otherwise we let ADMIN's edit any user
if (!isAdmin) {
if (safeQuery.data.id !== userId) res.status(401).json({ message: "Unauthorized" });
} else {
switch (method) {
case "GET":
/**

View File

@ -4,7 +4,8 @@ import prisma from "@calcom/prisma";
import { withMiddleware } from "@lib/helpers/withMiddleware";
import { UserResponse, UsersResponse } from "@lib/types";
import { schemaUserReadPublic } from "@lib/validations/user";
import { isAdminGuard } from "@lib/utils/isAdmin";
import { schemaUserReadPublic, schemaUserCreateBodyParams } from "@lib/validations/user";
/**
* @swagger
@ -26,34 +27,34 @@ async function getAllorCreateUser(
{ userId, method, body }: NextApiRequest,
res: NextApiResponse<UsersResponse | UserResponse>
) {
const isAdmin = await isAdminGuard(userId);
if (method === "GET") {
const data = await prisma.user.findMany({
where: {
id: userId,
},
});
const users = data.map((user) => schemaUserReadPublic.parse(user));
if (users) res.status(200).json({ users });
else
(error: Error) =>
res.status(404).json({
message: "No Users were found",
error,
});
if (!isAdmin) {
// If user is not ADMIN, return only his data.
const data = await prisma.user.findMany({ where: { id: userId } });
const users = data.map((user) => schemaUserReadPublic.parse(user));
if (users) res.status(200).json({ users });
} else {
// If user is admin, return all users.
const data = await prisma.user.findMany({});
const users = data.map((user) => schemaUserReadPublic.parse(user));
if (users) res.status(200).json({ users });
}
} else if (method === "POST") {
// If user is not ADMIN, return unauthorized.
if (!isAdmin) res.status(401).json({ message: "You are not authorized" });
else {
const safeBody = schemaUserCreateBodyParams.safeParse(body);
if (!safeBody.success) {
res.status(400).json({ message: "Your body was invalid" });
return;
}
const user = await prisma.user.create({
data: safeBody.data,
});
res.status(201).json({ user });
}
}
// else if (method === "POST") {
// const isAdmin = await prisma.user
// .findUnique({ where: { id: userId } })
// .then((user) => user?.role === "ADMIN");
// if (!isAdmin) res.status(401).json({ message: "You are not authorized" });
// else {
// const user = await prisma.user.create({
// data: schemaUserReadPublic.parse(body),
// });
// res.status(201).json({ user });
// }
// }
}
// No POST endpoint for users for now as a regular user you're expected to signup.
export default withMiddleware("HTTP_GET_OR_POST")(getAllorCreateUser);