Fixes disable branding for teams and users (#5913)
* Adding new nextauth object * Use correct brand hidden values * Move check to getprops instead of a function * Update apps/web/components/booking/pages/AvailabilityPage.tsx * Update apps/web/components/booking/pages/AvailabilityPage.tsx * Update apps/web/pages/[user]/[type].tsx Co-authored-by: Leo Giovanetti <hello@leog.me> * Update apps/web/pages/api/auth/[...nextauth].tsx Co-authored-by: Leo Giovanetti <hello@leog.me> * Update apps/web/pages/api/auth/[...nextauth].tsx Co-authored-by: Leo Giovanetti <hello@leog.me> Co-authored-by: Leo Giovanetti <hello@leog.me>
This commit is contained in:
co-authored by
Leo Giovanetti
parent
c3122c82e4
commit
f4ed345a87
@@ -442,7 +442,7 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{(!eventType.users[0] || !isBrandingHidden(eventType.users[0])) && !isEmbed && <PoweredByCal />}
|
||||
{(!restProps.isBrandingHidden || isEmbed) && <PoweredByCal />}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { User } from "@prisma/client";
|
||||
|
||||
export function isBrandingHidden<TUser extends Pick<User, "hideBranding" | "plan">>(user: TUser) {
|
||||
return user.hideBranding && user.plan !== "FREE";
|
||||
export function isBrandingHidden(hideBrandingSetting: boolean, belongsToActiveTeam: boolean) {
|
||||
return belongsToActiveTeam && hideBrandingSetting;
|
||||
}
|
||||
|
||||
@@ -10,8 +10,9 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { User } from "@calcom/prisma/client";
|
||||
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
import { EventTypeMetaDataSchema, teamMetadataSchema } from "@calcom/prisma/zod-utils";
|
||||
|
||||
import { isBrandingHidden } from "@lib/isBrandingHidden";
|
||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
import { EmbedProps } from "@lib/withEmbedSsr";
|
||||
|
||||
@@ -110,6 +111,11 @@ async function getUserPageProps(context: GetStaticPropsContext) {
|
||||
},
|
||||
],
|
||||
},
|
||||
teams: {
|
||||
include: {
|
||||
team: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -150,6 +156,13 @@ async function getUserPageProps(context: GetStaticPropsContext) {
|
||||
locations: privacyFilteredLocations(locations),
|
||||
descriptionAsSafeHTML: eventType.description ? md.render(eventType.description) : null,
|
||||
});
|
||||
// Check if the user you are logging into has any active teams
|
||||
const hasActiveTeam =
|
||||
user.teams.filter((m) => {
|
||||
const metadata = teamMetadataSchema.safeParse(m.team.metadata);
|
||||
if (metadata.success && metadata.data?.subscriptionId) return false;
|
||||
return true;
|
||||
}).length > 0;
|
||||
|
||||
return {
|
||||
props: {
|
||||
@@ -167,6 +180,7 @@ async function getUserPageProps(context: GetStaticPropsContext) {
|
||||
away: user?.away,
|
||||
isDynamic: false,
|
||||
trpcState: ssg.dehydrate(),
|
||||
isBrandingHidden: isBrandingHidden(user.hideBranding, hasActiveTeam),
|
||||
},
|
||||
revalidate: 10, // seconds
|
||||
};
|
||||
@@ -262,6 +276,7 @@ async function getDynamicGroupPageProps(context: GetStaticPropsContext) {
|
||||
isDynamic: true,
|
||||
away: false,
|
||||
trpcState: ssg.dehydrate(),
|
||||
isBrandingHidden: false, // I think we should always show branding for dynamic groups - saves us checking every single user
|
||||
},
|
||||
revalidate: 10, // seconds
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ import { defaultCookies } from "@calcom/lib/default-cookies";
|
||||
import rateLimit from "@calcom/lib/rateLimit";
|
||||
import { serverConfig } from "@calcom/lib/serverConfig";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
|
||||
|
||||
import CalComAdapter from "@lib/auth/next-auth-custom-adapter";
|
||||
import { randomString } from "@lib/random";
|
||||
@@ -63,6 +64,11 @@ const providers: Provider[] = [
|
||||
password: true,
|
||||
twoFactorEnabled: true,
|
||||
twoFactorSecret: true,
|
||||
teams: {
|
||||
include: {
|
||||
team: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -116,6 +122,13 @@ const providers: Provider[] = [
|
||||
intervalInMs: 60 * 1000, // 1 minute
|
||||
});
|
||||
await limiter.check(10, user.email); // 10 requests per minute
|
||||
// Check if the user you are logging into has any active teams
|
||||
const hasActiveTeams =
|
||||
user.teams.filter((m) => {
|
||||
const metadata = teamMetadataSchema.safeParse(m.team.metadata);
|
||||
if (metadata.success && metadata.data?.subscriptionId) return false;
|
||||
return true;
|
||||
}).length > 0;
|
||||
|
||||
// authentication success- but does it meet the minimum password requirements?
|
||||
if (user.role === "ADMIN" && !isPasswordValid(credentials.password, false, true)) {
|
||||
@@ -125,6 +138,7 @@ const providers: Provider[] = [
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
role: "USER",
|
||||
belongsToActiveTeam: hasActiveTeams,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -134,6 +148,7 @@ const providers: Provider[] = [
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
role: user.role,
|
||||
belongsToActiveTeam: hasActiveTeams,
|
||||
};
|
||||
},
|
||||
}),
|
||||
@@ -272,6 +287,7 @@ export default NextAuth({
|
||||
email: user.email,
|
||||
role: user.role,
|
||||
impersonatedByUID: user?.impersonatedByUID,
|
||||
belongsToActiveTeam: user?.belongsToActiveTeam,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -307,6 +323,7 @@ export default NextAuth({
|
||||
email: existingUser.email,
|
||||
role: existingUser.role,
|
||||
impersonatedByUID: token.impersonatedByUID as number,
|
||||
belongsToActiveTeam: token?.belongsToActiveTeam as boolean,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -324,6 +341,7 @@ export default NextAuth({
|
||||
username: token.username as string,
|
||||
role: token.role as UserPermissionRole,
|
||||
impersonatedByUID: token.impersonatedByUID as number,
|
||||
belongsToActiveTeam: token?.belongsToActiveTeam as boolean,
|
||||
},
|
||||
};
|
||||
return calendsoSession;
|
||||
|
||||
@@ -166,6 +166,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
||||
previousPage: context.req.headers.referer ?? null,
|
||||
booking,
|
||||
users: [user.username],
|
||||
isBrandingHidden: user.hideBranding,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useSession } from "next-auth/react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
@@ -41,7 +42,7 @@ const SkeletonLoader = () => {
|
||||
|
||||
const AppearanceView = () => {
|
||||
const { t } = useLocale();
|
||||
|
||||
const session = useSession();
|
||||
const utils = trpc.useContext();
|
||||
const { data: user, isLoading } = trpc.viewer.me.useQuery();
|
||||
|
||||
@@ -180,6 +181,7 @@ const AppearanceView = () => {
|
||||
<div className="flex-none">
|
||||
<Switch
|
||||
id="hideBranding"
|
||||
disabled={!session.data?.user.belongsToActiveTeam}
|
||||
onCheckedChange={(checked) =>
|
||||
formMethods.setValue("hideBranding", checked, { shouldDirty: true })
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
||||
name: true,
|
||||
slug: true,
|
||||
logo: true,
|
||||
hideBranding: true,
|
||||
eventTypes: {
|
||||
where: {
|
||||
slug: typeParam,
|
||||
@@ -160,6 +161,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
||||
previousPage: context.req.headers.referer ?? null,
|
||||
booking,
|
||||
trpcState: ssg.dehydrate(),
|
||||
isBrandingHidden: team.hideBranding,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -11,7 +11,8 @@ const teamIdschema = z.object({
|
||||
|
||||
const auditAndReturnNextUser = async (
|
||||
impersonatedUser: Pick<User, "id" | "username" | "email" | "name" | "role">,
|
||||
impersonatedByUID: number
|
||||
impersonatedByUID: number,
|
||||
hasTeam?: boolean
|
||||
) => {
|
||||
// Log impersonations for audit purposes
|
||||
await prisma.impersonations.create({
|
||||
@@ -36,6 +37,7 @@ const auditAndReturnNextUser = async (
|
||||
name: impersonatedUser.name,
|
||||
role: impersonatedUser.role,
|
||||
impersonatedByUID,
|
||||
belongsToActiveTeam: hasTeam,
|
||||
};
|
||||
|
||||
return obj;
|
||||
@@ -103,7 +105,11 @@ const ImpersonationProvider = CredentialsProvider({
|
||||
if (impersonatedUser.disableImpersonation) {
|
||||
throw new Error("This user has disabled Impersonation.");
|
||||
}
|
||||
return auditAndReturnNextUser(impersonatedUser, session?.user.id as number);
|
||||
return auditAndReturnNextUser(
|
||||
impersonatedUser,
|
||||
session?.user.id as number,
|
||||
impersonatedUser.teams.length > 0 // If the user has any teams, they belong to an active team and we can set the hasActiveTeam ctx to true
|
||||
);
|
||||
}
|
||||
|
||||
if (!teamId) throw new Error("You do not have permission to do this.");
|
||||
@@ -137,7 +143,11 @@ const ImpersonationProvider = CredentialsProvider({
|
||||
throw new Error("You do not have permission to do this.");
|
||||
}
|
||||
|
||||
return auditAndReturnNextUser(impersonatedUser, session?.user.id as number);
|
||||
return auditAndReturnNextUser(
|
||||
impersonatedUser,
|
||||
session?.user.id as number,
|
||||
impersonatedUser.teams.length > 0 // If the user has any teams, they belong to an active team and we can set the hasActiveTeam ctx to true
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Vendored
+1
@@ -14,6 +14,7 @@ declare module "next-auth" {
|
||||
emailVerified?: PrismaUser["emailVerified"];
|
||||
email_verified?: boolean;
|
||||
impersonatedByUID?: number;
|
||||
belongsToActiveTeam?: boolean;
|
||||
username?: PrismaUser["username"];
|
||||
role?: PrismaUser["role"];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user