Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
This commit is contained in:
Hariom Balhara 2023-07-26 21:13:33 +05:30 committed by GitHub
parent bd40dcfa4d
commit 83d98a1ac4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 51 additions and 14 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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: {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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