Revert "Slack Oauth + verify sig"

This reverts commit ee95795e0f.
monorepo/app-store-teams-integration
sean-brydon 2022-03-02 22:01:04 +00:00
parent ee95795e0f
commit c9a3c5789e
20 changed files with 1272 additions and 613 deletions

View File

@ -1,4 +1,4 @@
const withTM = require("next-transpile-modules")([
const withTM = require("@vercel/edge-functions-ui/transpile")([
"@calcom/app-store",
"@calcom/lib",
"@calcom/prisma",

View File

@ -1,65 +0,0 @@
import { createHmac } from "crypto";
import dayjs from "dayjs";
import { NextApiRequest, NextApiResponse } from "next";
import { stringify } from "querystring";
import appStore from "@calcom/app-store";
import { HttpError } from "@lib/core/http/error";
const signingSecret = process.env.SLACK_SIGNING_SECRET;
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { args } = req.query;
const body = req.body;
const timeStamp = req.headers["x-slack-request-timestamp"] as string; // Always returns a string and not a string[]
const slackSignature = req.headers["x-slack-signature"] as string;
const currentTime = dayjs().unix();
if (!timeStamp) {
return res.status(400).json({ message: "Missing X-Slack-Request-Timestamp header" });
}
if (!signingSecret) {
return res.status(400).json({ message: "Missing process.env.SLACK_SIGNING_SECRET" });
}
if (Math.abs(currentTime - parseInt(timeStamp)) > 60 * 5) {
return res.status(400).json({ message: "Request is too old" });
}
const signature_base = `v0:${timeStamp}:${stringify(body)}`;
const signed_sig = "v0=" + createHmac("sha256", signingSecret).update(signature_base).digest("hex");
if (signed_sig !== slackSignature) {
return res.status(400).json({ message: "Invalid signature" });
}
if (!Array.isArray(args)) {
return res.status(404).json({ message: `API route not found` });
}
const [_appName, apiEndpoint] = args;
const appName = _appName.split("_").join("");
try {
const handler = appStore[appName].api[apiEndpoint];
console.log(`API: ${appName}/${apiEndpoint}`);
if (typeof handler !== "function")
throw new HttpError({ statusCode: 404, message: `API handler not found` });
const response = await handler(req, res);
console.log("response", response);
res.status(200);
} catch (error) {
console.error(error);
if (error instanceof HttpError) {
return res.status(error.statusCode).json({ message: error.message });
}
return res.status(404).json({ message: `API handler not found` });
}
// Return a response to acknowledge receipt of the event
}

View File

@ -1,31 +0,0 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 270 270" style="enable-background:new 0 0 270 270;" xml:space="preserve">
<style type="text/css">
.st0{fill:#E01E5A;}
.st1{fill:#36C5F0;}
.st2{fill:#2EB67D;}
.st3{fill:#ECB22E;}
</style>
<g>
<g>
<path class="st0" d="M99.4,151.2c0,7.1-5.8,12.9-12.9,12.9c-7.1,0-12.9-5.8-12.9-12.9c0-7.1,5.8-12.9,12.9-12.9h12.9V151.2z"/>
<path class="st0" d="M105.9,151.2c0-7.1,5.8-12.9,12.9-12.9s12.9,5.8,12.9,12.9v32.3c0,7.1-5.8,12.9-12.9,12.9
s-12.9-5.8-12.9-12.9V151.2z"/>
</g>
<g>
<path class="st1" d="M118.8,99.4c-7.1,0-12.9-5.8-12.9-12.9c0-7.1,5.8-12.9,12.9-12.9s12.9,5.8,12.9,12.9v12.9H118.8z"/>
<path class="st1" d="M118.8,105.9c7.1,0,12.9,5.8,12.9,12.9s-5.8,12.9-12.9,12.9H86.5c-7.1,0-12.9-5.8-12.9-12.9
s5.8-12.9,12.9-12.9H118.8z"/>
</g>
<g>
<path class="st2" d="M170.6,118.8c0-7.1,5.8-12.9,12.9-12.9c7.1,0,12.9,5.8,12.9,12.9s-5.8,12.9-12.9,12.9h-12.9V118.8z"/>
<path class="st2" d="M164.1,118.8c0,7.1-5.8,12.9-12.9,12.9c-7.1,0-12.9-5.8-12.9-12.9V86.5c0-7.1,5.8-12.9,12.9-12.9
c7.1,0,12.9,5.8,12.9,12.9V118.8z"/>
</g>
<g>
<path class="st3" d="M151.2,170.6c7.1,0,12.9,5.8,12.9,12.9c0,7.1-5.8,12.9-12.9,12.9c-7.1,0-12.9-5.8-12.9-12.9v-12.9H151.2z"/>
<path class="st3" d="M151.2,164.1c-7.1,0-12.9-5.8-12.9-12.9c0-7.1,5.8-12.9,12.9-12.9h32.3c7.1,0,12.9,5.8,12.9,12.9
c0,7.1-5.8,12.9-12.9,12.9H151.2z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,13 +1,11 @@
import * as example from "./_example";
import * as dailyvideo from "./dailyvideo";
import * as slackapp from "./slackapp";
import * as zoomvideo from "./zoomvideo";
const appStore = {
example,
dailyvideo,
zoomvideo,
slackapp,
};
export default appStore;

View File

@ -1,35 +0,0 @@
import { InstallProvider } from "@slack/oauth";
import type { NextApiRequest, NextApiResponse } from "next";
import { stringify } from "querystring";
import { BASE_URL } from "@calcom/lib/constants";
import prisma from "@calcom/prisma";
const client_id = process.env.SLACK_CLIENT_ID;
const client_secret = process.env.SLACK_CLIENT_SECRET;
const scopes = ["commands"];
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === "GET") {
// Get user
await prisma.user.findFirst({
rejectOnNotFound: true,
where: {
id: req.session?.user?.id,
},
select: {
id: true,
},
});
const params = {
client_id,
scope: scopes.join(","),
};
const query = stringify(params);
const url = `https://slack.com/oauth/v2/authorize?${query}&user_`;
// const url =
// "https://slack.com/oauth/v2/authorize?client_id=3194129032064.3178385871204&scope=chat:write,commands&user_scope=";
res.status(200).json({ url });
}
res.status(404).json({ error: "Not Found" });
}

View File

@ -1,46 +0,0 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { stringify } from "querystring";
import prisma from "@calcom/prisma";
const client_id = process.env.SLACK_CLIENT_ID;
const client_secret = process.env.SLACK_CLIENT_SECRET;
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === "GET") {
// Get user
const { code } = req.query;
console.log(req.query);
if (!code) {
res.redirect("/apps/installed"); // Redirect to where the user was if they cancel the signup or if the oauth fails
}
const query = {
client_secret,
client_id,
code,
};
const params = stringify(query);
console.log("params", params);
const url = `https://slack.com/api/oauth.v2.access?${params}`;
const result = await fetch(url);
const responseBody = await result.json();
await prisma.user.update({
where: {
id: req.session?.user.id,
},
data: {
credentials: {
create: {
type: "slack_app",
key: responseBody,
},
},
},
});
res.redirect("/apps/installed");
}
}

View File

@ -1,23 +0,0 @@
import type { NextApiRequest, NextApiResponse } from "next";
import prisma from "@calcom/prisma";
import { createEvent } from "../lib";
export enum SlackAppCommands {
CREATE_EVENT = "create-event",
}
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === "POST") {
const command = req.body.command.split("/").pop();
switch (command) {
case SlackAppCommands.CREATE_EVENT:
return await createEvent(req, res);
default:
return res.status(404).json({ message: `Command not found` });
}
}
res.status(400).json({ message: "Invalid request" });
}

View File

@ -1,3 +0,0 @@
export { default as add } from "./add";
export { default as callback } from "./callback";
export { default as commandHandler } from "./commandHandler";

View File

@ -1,25 +0,0 @@
import type { App } from "@calcom/types/App";
import _package from "./package.json";
export const metadata = {
name: _package.name,
description: _package.description,
installed: !!(process.env.SLACK_CLIENT_ID && process.env.SLACK_CLIENT_SECRET),
category: "video",
imageSrc: "apps/slack.svg",
label: "Slack App",
logo: "/apps/slack.svg",
publisher: "Cal.com",
rating: 5,
reviews: 69,
slug: "slack_app",
title: "Slack App",
trending: true,
type: "slack_app",
url: "https://slack.com/",
variant: "conferencing",
verified: true,
} as App;
export * as api from "./api";

View File

@ -1,34 +0,0 @@
import { NextApiRequest, NextApiResponse } from "next";
import { CreateEventModal } from "./views";
export default async function createEvents(req: NextApiRequest, res: NextApiResponse) {
const body = req.body;
const data = await prisma.credential.findFirst({
where: {
type: "slack_app",
key: {
path: ["authed_user", "id"],
equals: body.user_id,
},
},
include: {
user: {
select: {
username: true,
eventTypes: {
select: {
id: true,
title: true,
},
},
},
},
},
});
if (!data) res.status(200).json({ message: "No user found" });
res.status(200).json(CreateEventModal(data));
}

View File

@ -1,2 +0,0 @@
export { default as locationOption } from "./locationOption";
export { default as createEvent } from "./createEvent";

View File

@ -1,9 +0,0 @@
import { LocationType } from "@calcom/lib/location";
const locationOption = {
value: LocationType.Slack,
label: "Slack App",
disabled: false,
};
export default locationOption;

View File

@ -1,49 +0,0 @@
import { User } from "@prisma/client";
import { Modal, Blocks, Elements, Bits } from "slack-block-builder";
const CreateEventModal = (
data:
| (Credential & {
user: {
username: string | null;
eventTypes: {
id: number;
title: string;
}[];
} | null;
})
| null
) => {
return Modal({ title: "Cal.com", submit: "Create" })
.blocks(
Blocks.Section({ text: `Hey there, ${data?.user?.username}!` }),
Blocks.Divider(),
Blocks.Input({ label: "Which event would you like to create?" }).element(
Elements.StaticSelect({ placeholder: "Which event would you like to create?" })
.actionId("events_types")
.options(
data?.user?.eventTypes.map((item: any) =>
Bits.Option({ text: item.title ?? "No Name", value: item.id.toString() })
)
)
),
Blocks.Input({ label: "Who would you like to invite to your event?" }).element(
Elements.UserMultiSelect({ placeholder: "Who would you like to invite to your event?" }).actionId(
"invite_users"
)
),
Blocks.Input({ label: "When would this event be?" }).element(
Elements.DatePicker({ placeholder: "Select Date" }).actionId("event_date")
),
Blocks.Input({ label: "What time would you like to start?" }).element(
Elements.TimePicker({ placeholder: "Select Time" }).actionId("event_start_time")
)
)
.buildToJSON();
};
export default CreateEventModal;
// Elements.StaticSelect({ placeholder: "Which event would you like to create?" })
// .actionId("events_types")
// .options(data.events.map((item) => Bits.Option({ text: item.name, value: item.id })))

View File

@ -1 +0,0 @@
export { default as CreateEventModal } from "./CreateEventModal";

View File

@ -1,15 +0,0 @@
{
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"name": "@calcom/slackapp",
"version": "0.0.0",
"main": "./index.ts",
"description": "This is a package for the intergration of slack into the app-store",
"dependencies": {
"@calcom/prisma": "*",
"slack-block-builder": "^2.5.0"
},
"devDependencies": {
"@calcom/types": "*"
}
}

View File

@ -1,31 +0,0 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 270 270" style="enable-background:new 0 0 270 270;" xml:space="preserve">
<style type="text/css">
.st0{fill:#E01E5A;}
.st1{fill:#36C5F0;}
.st2{fill:#2EB67D;}
.st3{fill:#ECB22E;}
</style>
<g>
<g>
<path class="st0" d="M99.4,151.2c0,7.1-5.8,12.9-12.9,12.9c-7.1,0-12.9-5.8-12.9-12.9c0-7.1,5.8-12.9,12.9-12.9h12.9V151.2z"/>
<path class="st0" d="M105.9,151.2c0-7.1,5.8-12.9,12.9-12.9s12.9,5.8,12.9,12.9v32.3c0,7.1-5.8,12.9-12.9,12.9
s-12.9-5.8-12.9-12.9V151.2z"/>
</g>
<g>
<path class="st1" d="M118.8,99.4c-7.1,0-12.9-5.8-12.9-12.9c0-7.1,5.8-12.9,12.9-12.9s12.9,5.8,12.9,12.9v12.9H118.8z"/>
<path class="st1" d="M118.8,105.9c7.1,0,12.9,5.8,12.9,12.9s-5.8,12.9-12.9,12.9H86.5c-7.1,0-12.9-5.8-12.9-12.9
s5.8-12.9,12.9-12.9H118.8z"/>
</g>
<g>
<path class="st2" d="M170.6,118.8c0-7.1,5.8-12.9,12.9-12.9c7.1,0,12.9,5.8,12.9,12.9s-5.8,12.9-12.9,12.9h-12.9V118.8z"/>
<path class="st2" d="M164.1,118.8c0,7.1-5.8,12.9-12.9,12.9c-7.1,0-12.9-5.8-12.9-12.9V86.5c0-7.1,5.8-12.9,12.9-12.9
c7.1,0,12.9,5.8,12.9,12.9V118.8z"/>
</g>
<g>
<path class="st3" d="M151.2,170.6c7.1,0,12.9,5.8,12.9,12.9c0,7.1-5.8,12.9-12.9,12.9c-7.1,0-12.9-5.8-12.9-12.9v-12.9H151.2z"/>
<path class="st3" d="M151.2,164.1c-7.1,0-12.9-5.8-12.9-12.9c0-7.1,5.8-12.9,12.9-12.9h32.3c7.1,0,12.9,5.8,12.9,12.9
c0,7.1-5.8,12.9-12.9,12.9H151.2z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -8,5 +8,4 @@ export enum LocationType {
Jitsi = "integrations:jitsi",
Huddle01 = "integrations:huddle01",
Tandem = "integrations:tandem",
Slack = "integrations:slack",
}

View File

@ -8,7 +8,6 @@ datasource db {
generator client {
provider = "prisma-client-js"
previewFeatures = ["filterJson"]
}
generator zod {

View File

@ -9,7 +9,7 @@ export interface App {
* */
installed: boolean;
/** The app type */
type: `${string}_calendar` | `${string}_payment` | `${string}_video` | `${string}_web3` | `${string}_app`;
type: `${string}_calendar` | `${string}_payment` | `${string}_video` | `${string}_web3`;
/** The display name for the app, TODO settle between this or name */
title: string;
/** The display name for the app */
@ -19,7 +19,7 @@ export interface App {
/** The icon to display in /apps/installed */
imageSrc: string;
/** TODO determine if we should use this instead of category */
variant: "calendar" | "payment" | "conferencing" | "app";
variant: "calendar" | "payment" | "conferencing";
label: string;
/** The slug for the app store public page inside `/apps/[slug] */
slug: string;

1504
yarn.lock

File diff suppressed because it is too large Load Diff