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 <peeroke@gmail.com>
This commit is contained in:
Hariom Balhara
2024-07-30 11:52:10 +00:00
committed by GitHub
co-authored by sean-brydon Peer Richelsen
parent 58cabe974c
commit 40df4cf511
2 changed files with 52 additions and 2 deletions
@@ -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<TrpcSessionUser>;
@@ -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 || "";
@@ -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;