fix: Apps (#10394)
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
This commit is contained in:
parent
bd40dcfa4d
commit
83d98a1ac4
|
@ -2,6 +2,7 @@ import type { NextApiRequest, NextApiResponse } from "next";
|
|||
import type { Session } from "next-auth";
|
||||
|
||||
import getInstalledAppPath from "@calcom/app-store/_utils/getInstalledAppPath";
|
||||
import { throwIfNotHaveAdminAccessToTeam } from "@calcom/app-store/_utils/throwIfNotHaveAdminAccessToTeam";
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { deriveAppDictKeyFromType } from "@calcom/lib/deriveAppDictKeyFromType";
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
|
@ -37,6 +38,9 @@ const defaultIntegrationAddHandler = async ({
|
|||
throw new Error("App is already installed");
|
||||
}
|
||||
}
|
||||
|
||||
await throwIfNotHaveAdminAccessToTeam({ teamId: teamId ?? null, userId: user.id });
|
||||
|
||||
await createCredential({ user: user, appType, slug, teamId });
|
||||
};
|
||||
|
||||
|
@ -72,7 +76,6 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||
return res.status(200);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
if (error instanceof HttpError) {
|
||||
return res.status(error.statusCode).json({ message: error.message });
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import type { NextApiRequest, NextApiResponse } from "next";
|
|||
import { z } from "zod";
|
||||
|
||||
import getAppKeysFromSlug from "@calcom/app-store/_utils/getAppKeysFromSlug";
|
||||
import { throwIfNotHaveAdminAccessToTeam } from "@calcom/app-store/_utils/throwIfNotHaveAdminAccessToTeam";
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl";
|
||||
|
@ -22,7 +23,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
const { code, state } = req.query;
|
||||
const parsedState = stateSchema.parse(JSON.parse(state as string));
|
||||
const { teamId } = parsedState;
|
||||
|
||||
await throwIfNotHaveAdminAccessToTeam({ teamId: Number(teamId) ?? null, userId: session.user.id });
|
||||
if (code && typeof code !== "string") {
|
||||
res.status(400).json({ message: "`code` must be a string" });
|
||||
return;
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import type { NextApiRequest } from "next";
|
||||
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
import { decodeOAuthState } from "./decodeOAuthState";
|
||||
import { throwIfNotHaveAdminAccessToTeam } from "./throwIfNotHaveAdminAccessToTeam";
|
||||
|
||||
/**
|
||||
* This function is used to create app credentials for either a user or a team
|
||||
|
@ -19,8 +21,12 @@ const createOAuthAppCredential = async (
|
|||
req: NextApiRequest
|
||||
) => {
|
||||
const userId = req.session?.user.id;
|
||||
if (!userId) {
|
||||
throw new HttpError({ statusCode: 401, message: "You must be logged in to do this" });
|
||||
}
|
||||
// For OAuth flows, see if a teamId was passed through the state
|
||||
const state = decodeOAuthState(req);
|
||||
|
||||
if (state?.teamId) {
|
||||
// Check that the user belongs to the team
|
||||
const team = await prisma.team.findFirst({
|
||||
|
@ -50,6 +56,8 @@ const createOAuthAppCredential = async (
|
|||
return;
|
||||
}
|
||||
|
||||
await throwIfNotHaveAdminAccessToTeam({ teamId: state?.teamId ?? null, userId });
|
||||
|
||||
await prisma.credential.create({
|
||||
data: {
|
||||
type: appData.type,
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import getUserAdminTeams from "@calcom/features/ee/teams/lib/getUserAdminTeams";
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
|
||||
export const throwIfNotHaveAdminAccessToTeam = async ({
|
||||
teamId,
|
||||
userId,
|
||||
}: {
|
||||
teamId: number | null;
|
||||
userId: number;
|
||||
}) => {
|
||||
if (!teamId) {
|
||||
return;
|
||||
}
|
||||
const teamsUserHasAdminAccessFor = await getUserAdminTeams({ userId });
|
||||
const hasAdminAccessToTeam = teamsUserHasAdminAccessFor.some((team) => team.id === teamId);
|
||||
|
||||
if (!hasAdminAccessToTeam) {
|
||||
throw new HttpError({ statusCode: 401, message: "You must be an admin of the team to do this" });
|
||||
}
|
||||
};
|
|
@ -3,6 +3,7 @@ import type { NextApiRequest, NextApiResponse } from "next";
|
|||
import prisma from "@calcom/prisma";
|
||||
|
||||
import getInstalledAppPath from "../../_utils/getInstalledAppPath";
|
||||
import { throwIfNotHaveAdminAccessToTeam } from "../../_utils/throwIfNotHaveAdminAccessToTeam";
|
||||
|
||||
/**
|
||||
* This is an example endpoint for an app, these will run under `/api/integrations/[...args]`
|
||||
|
@ -13,10 +14,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
if (!req.session?.user?.id) {
|
||||
return res.status(401).json({ message: "You must be logged in to do this" });
|
||||
}
|
||||
|
||||
const userId = req.session.user.id;
|
||||
const appType = "giphy_other";
|
||||
const credentialOwner = req.query.teamId
|
||||
? { teamId: Number(req.query.teamId) }
|
||||
: { userId: req.session.user.id };
|
||||
const teamId = Number(req.query.teamId);
|
||||
const credentialOwner = req.query.teamId ? { teamId } : { userId: req.session.user.id };
|
||||
|
||||
await throwIfNotHaveAdminAccessToTeam({ teamId: teamId ?? null, userId });
|
||||
|
||||
try {
|
||||
const alreadyInstalled = await prisma.credential.findFirst({
|
||||
where: {
|
||||
|
|
|
@ -47,7 +47,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
// set expiry date as offset from current time.
|
||||
hubspotToken.expiryDate = Math.round(Date.now() + hubspotToken.expiresIn * 1000);
|
||||
|
||||
createOAuthAppCredential({ appId: "hubspot", type: "hubspot_other_calendar" }, hubspotToken as any, req);
|
||||
await createOAuthAppCredential({ appId: "hubspot", type: "hubspot_other_calendar" }, hubspotToken as any, req);
|
||||
|
||||
const state = decodeOAuthState(req);
|
||||
res.redirect(
|
||||
|
|
|
@ -93,7 +93,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
await prisma.credential.deleteMany({ where: { id: { in: credentialIdsToDelete }, userId } });
|
||||
}
|
||||
|
||||
createOAuthAppCredential({ appId: "msteams", type: "office365_video" }, responseBody, req);
|
||||
await createOAuthAppCredential({ appId: "msteams", type: "office365_video" }, responseBody, req);
|
||||
|
||||
const state = decodeOAuthState(req);
|
||||
return res.redirect(
|
||||
|
|
|
@ -38,7 +38,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
|
||||
const salesforceTokenInfo = await conn.oauth2.requestToken(code as string);
|
||||
|
||||
createOAuthAppCredential(
|
||||
await createOAuthAppCredential(
|
||||
{ appId: "salesforce", type: "salesforce_other_calendar" },
|
||||
salesforceTokenInfo as any,
|
||||
req
|
||||
|
|
|
@ -44,7 +44,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
data["default_currency"] = account.default_currency;
|
||||
}
|
||||
|
||||
createOAuthAppCredential(
|
||||
await createOAuthAppCredential(
|
||||
{ appId: "stripe", type: "stripe_payment" },
|
||||
data as unknown as Prisma.InputJsonObject,
|
||||
req
|
||||
|
|
|
@ -60,7 +60,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
responseBody.expiry_date = Math.round(Date.now() + responseBody.expires_in * 1000);
|
||||
delete responseBody.expires_in;
|
||||
|
||||
createOAuthAppCredential({ appId: "tandem", type: "tandem_video" }, responseBody, req);
|
||||
await createOAuthAppCredential({ appId: "tandem", type: "tandem_video" }, responseBody, req);
|
||||
|
||||
res.redirect(getInstalledAppPath({ variant: "conferencing", slug: "tandem" }));
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
await prisma.credential.deleteMany({ where: { id: { in: credentialIdsToDelete }, userId } });
|
||||
}
|
||||
|
||||
createOAuthAppCredential({ appId: config.slug, type: config.type }, responseBody, req);
|
||||
await createOAuthAppCredential({ appId: config.slug, type: config.type }, responseBody, req);
|
||||
|
||||
res.redirect(getInstalledAppPath({ variant: config.variant, slug: config.slug }));
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
tokenInfo.data.expiryDate = Math.round(Date.now() + tokenInfo.data.expires_in);
|
||||
tokenInfo.data.accountServer = accountsServer;
|
||||
|
||||
createOAuthAppCredential({ appId: appConfig.slug, type: appConfig.type }, tokenInfo.data, req);
|
||||
await createOAuthAppCredential({ appId: appConfig.slug, type: appConfig.type }, tokenInfo.data, req);
|
||||
|
||||
const state = decodeOAuthState(req);
|
||||
res.redirect(
|
||||
|
|
|
@ -51,7 +51,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
zohoCrmTokenInfo.data.expiryDate = Math.round(Date.now() + 60 * 60);
|
||||
zohoCrmTokenInfo.data.accountServer = req.query["accounts-server"];
|
||||
|
||||
createOAuthAppCredential(
|
||||
await createOAuthAppCredential(
|
||||
{ appId: "zohocrm", type: "zohocrm_other_calendar" },
|
||||
zohoCrmTokenInfo.data as any,
|
||||
req
|
||||
|
|
|
@ -70,7 +70,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
await prisma.credential.deleteMany({ where: { id: { in: credentialIdsToDelete }, userId } });
|
||||
}
|
||||
|
||||
createOAuthAppCredential({ appId: "zoom", type: "zoom_video" }, responseBody, req);
|
||||
await createOAuthAppCredential({ appId: "zoom", type: "zoom_video" }, responseBody, req);
|
||||
|
||||
res.redirect(getInstalledAppPath({ variant: "conferencing", slug: "zoom" }));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user