From 40df4cf511b30caa35c1f1df354bb1967188b697 Mon Sep 17 00:00:00 2001 From: Hariom Balhara Date: Tue, 30 Jul 2024 17:22:10 +0530 Subject: [PATCH] fix: Add strict checks for email to create an organization (#15976) * Verify email first * Only allow company emails for org creation --------- Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Co-authored-by: Peer Richelsen --- .../viewer/organizations/create.handler.ts | 52 ++++++++++++++++++- .../viewer/teams/inviteMember/utils.ts | 2 +- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/packages/trpc/server/routers/viewer/organizations/create.handler.ts b/packages/trpc/server/routers/viewer/organizations/create.handler.ts index 5d45c57a36..a8ac94d78c 100644 --- a/packages/trpc/server/routers/viewer/organizations/create.handler.ts +++ b/packages/trpc/server/routers/viewer/organizations/create.handler.ts @@ -23,6 +23,47 @@ import type { TrpcSessionUser } from "../../../trpc"; import { BillingPeriod } from "./create.schema"; import type { TCreateInputSchema } from "./create.schema"; +/** + * We can only say for sure that the email is not a company email. We can't say for sure if it is a company email. + */ +function isNotACompanyEmail(email: string) { + // A list of popular @domains that can't be used to allow automatic acceptance of memberships to organization + const emailProviders = [ + "gmail.com", + "yahoo.com", + "outlook.com", + "hotmail.com", + "aol.com", + "icloud.com", + "mail.com", + "protonmail.com", + "zoho.com", + "yandex.com", + "gmx.com", + "fastmail.com", + "inbox.com", + "me.com", + "hushmail.com", + "live.com", + "rediffmail.com", + "tutanota.com", + "mail.ru", + "usa.com", + "qq.com", + "163.com", + "web.de", + "rocketmail.com", + "excite.com", + "lycos.com", + "outlook.co", + "hotmail.co.uk", + ]; + + const emailParts = email.split("@"); + if (emailParts.length < 2) return true; + return emailProviders.includes(emailParts[1]); +} + type CreateOptions = { ctx: { user: NonNullable; @@ -69,6 +110,7 @@ export const createHandler = async ({ input, ctx }: CreateOptions) => { }, }, }); + if (!loggedInUser) throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized." }); const IS_USER_ADMIN = loggedInUser.role === UserPermissionRole.ADMIN; @@ -87,6 +129,10 @@ export const createHandler = async ({ input, ctx }: CreateOptions) => { }); } + if (isNotACompanyEmail(orgOwnerEmail)) { + throw new TRPCError({ code: "BAD_REQUEST", message: "Use company email to create an organization" }); + } + const publishedTeams = loggedInUser.teams.filter((team) => !!team.team.slug); if (!IS_USER_ADMIN && publishedTeams.length < ORG_MINIMUM_PUBLISHED_TEAMS_SELF_SERVE && !isPlatform) { @@ -206,7 +252,11 @@ export const createHandler = async ({ input, ctx }: CreateOptions) => { // If we are making the loggedIn user the owner of the organization and he is already a part of an organization, we don't allow it because multi-org is not supported yet const isLoggedInUserOrgOwner = orgOwner.id === loggedInUser.id; if (ctx.user.profile.organizationId && isLoggedInUserOrgOwner) { - throw new TRPCError({ code: "FORBIDDEN", message: "User is part of an organization already" }); + throw new TRPCError({ code: "FORBIDDEN", message: "You are part of an organization already" }); + } + + if (!orgOwner.emailVerified) { + throw new TRPCError({ code: "FORBIDDEN", message: "You need to verify your email first" }); } const nonOrgUsernameForOwner = orgOwner.username || ""; diff --git a/packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts b/packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts index b619f11678..3c8fad0f24 100644 --- a/packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts +++ b/packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts @@ -248,7 +248,7 @@ export function getOrgConnectionInfo({ if (team.parentId || isOrg) { orgId = team.parentId || team.id; if (email.split("@")[1] == orgAutoAcceptDomain) { - // We discourage self-served organizations from being able to auto-accept feature by having a barrier of a fixed number of paying teams in the account for creating the organization + // We discourage self-served organizations from being able to use auto-accept feature by having a barrier of a fixed number of paying teams in the account for creating the organization // We can't put restriction of a published organization here because when we move teams during the onboarding of the organization, it isn't published at the moment and we really need those members to be auto-added // Further, sensitive operations like member editing and impersonating are disabled by default, unless reviewed by the ADMIN team autoAccept = !!orgVerified;