Disable Impersonation Option (#2880)
* Disable Impersonation * Update Description Copy Co-authored-by: Peer Richelsen <peeroke@gmail.com>
This commit is contained in:
parent
e16eee68b0
commit
1bf009f5f5
|
@ -0,0 +1,55 @@
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import Button from "@calcom/ui/Button";
|
||||
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import Badge from "@components/ui/Badge";
|
||||
|
||||
const DisableUserImpersonation = ({ disableImpersonation }: { disableImpersonation: boolean }) => {
|
||||
const utils = trpc.useContext();
|
||||
|
||||
const { t } = useLocale();
|
||||
|
||||
const mutation = trpc.useMutation("viewer.updateProfile", {
|
||||
onSuccess: async () => {
|
||||
showToast(t("your_user_profile_updated_successfully"), "success");
|
||||
await utils.invalidateQueries(["viewer.me"]);
|
||||
},
|
||||
async onSettled() {
|
||||
await utils.invalidateQueries(["viewer.i18n"]);
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col justify-between pt-9 pl-2 sm:flex-row">
|
||||
<div>
|
||||
<div className="flex flex-row items-center">
|
||||
<h2 className="font-cal text-lg font-medium leading-6 text-gray-900">
|
||||
{t("user_impersonation_heading")}
|
||||
</h2>
|
||||
<Badge className="ml-2 text-xs" variant={!disableImpersonation ? "success" : "gray"}>
|
||||
{!disableImpersonation ? t("enabled") : t("disabled")}
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-gray-500">{t("user_impersonation_description")}</p>
|
||||
</div>
|
||||
<div className="mt-5 sm:mt-0 sm:self-center">
|
||||
<Button
|
||||
type="submit"
|
||||
color="secondary"
|
||||
onClick={() =>
|
||||
!disableImpersonation
|
||||
? mutation.mutate({ disableImpersonation: true })
|
||||
: mutation.mutate({ disableImpersonation: false })
|
||||
}>
|
||||
{!disableImpersonation ? t("disable") : t("enable")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DisableUserImpersonation;
|
|
@ -32,6 +32,10 @@ const ImpersonationProvider = CredentialsProvider({
|
|||
throw new Error("This user does not exist");
|
||||
}
|
||||
|
||||
if (user.disableImpersonation) {
|
||||
throw new Error("This user has disabled Impersonation.");
|
||||
}
|
||||
|
||||
// Log impersonations for audit purposes
|
||||
await prisma.impersonations.create({
|
||||
data: {
|
||||
|
|
|
@ -11,6 +11,7 @@ import { trpc } from "@lib/trpc";
|
|||
import SettingsShell from "@components/SettingsShell";
|
||||
import Shell from "@components/Shell";
|
||||
import ChangePasswordSection from "@components/security/ChangePasswordSection";
|
||||
import DisableUserImpersonation from "@components/security/DisableUserImpersonation";
|
||||
import TwoFactorAuthSection from "@components/security/TwoFactorAuthSection";
|
||||
|
||||
export default function Security() {
|
||||
|
@ -39,6 +40,7 @@ export default function Security() {
|
|||
<ChangePasswordSection />
|
||||
<ApiKeyListContainer />
|
||||
<TwoFactorAuthSection twoFactorEnabled={user?.twoFactorEnabled || false} />
|
||||
<DisableUserImpersonation disableImpersonation={user?.disableImpersonation ?? true} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
@ -816,6 +816,8 @@
|
|||
"confirmation_page_gif": "Gif for confirmation page",
|
||||
"search": "Search",
|
||||
"impersonate": "Impersonate",
|
||||
"user_impersonation_heading":"User Impersonation",
|
||||
"user_impersonation_description":"Allows our support team to temporarily sign in as you to help us quickly resolve any issues you report to us.",
|
||||
"impersonate_user_tip": "All uses of this feature is audited.",
|
||||
"impersonating_user_warning": "Impersonating username \"{{user}}\".",
|
||||
"impersonating_stop_instructions": "<0>Click Here to stop</0>.",
|
||||
|
|
|
@ -44,6 +44,7 @@ async function getUserFromSession({
|
|||
hideBranding: true,
|
||||
avatar: true,
|
||||
twoFactorEnabled: true,
|
||||
disableImpersonation: true,
|
||||
identityProvider: true,
|
||||
brandColor: true,
|
||||
darkBrandColor: true,
|
||||
|
|
|
@ -86,6 +86,7 @@ const loggedInViewerRouter = createProtectedRouter()
|
|||
trialEndsAt: user.trialEndsAt,
|
||||
completedOnboarding: user.completedOnboarding,
|
||||
twoFactorEnabled: user.twoFactorEnabled,
|
||||
disableImpersonation: user.disableImpersonation,
|
||||
identityProvider: user.identityProvider,
|
||||
brandColor: user.brandColor,
|
||||
darkBrandColor: user.darkBrandColor,
|
||||
|
@ -681,6 +682,7 @@ const loggedInViewerRouter = createProtectedRouter()
|
|||
completedOnboarding: z.boolean().optional(),
|
||||
locale: z.string().optional(),
|
||||
timeFormat: z.number().optional(),
|
||||
disableImpersonation: z.boolean().optional(),
|
||||
}),
|
||||
async resolve({ input, ctx }) {
|
||||
const { user, prisma } = ctx;
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "users" ADD COLUMN "disableImpersonation" BOOLEAN NOT NULL DEFAULT false;
|
|
@ -119,59 +119,60 @@ enum UserPermissionRole {
|
|||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
username String? @unique
|
||||
name String?
|
||||
id Int @id @default(autoincrement())
|
||||
username String? @unique
|
||||
name String?
|
||||
/// @zod.email()
|
||||
email String @unique
|
||||
emailVerified DateTime?
|
||||
password String?
|
||||
bio String?
|
||||
avatar String?
|
||||
timeZone String @default("Europe/London")
|
||||
weekStart String @default("Sunday")
|
||||
email String @unique
|
||||
emailVerified DateTime?
|
||||
password String?
|
||||
bio String?
|
||||
avatar String?
|
||||
timeZone String @default("Europe/London")
|
||||
weekStart String @default("Sunday")
|
||||
// DEPRECATED - TO BE REMOVED
|
||||
startTime Int @default(0)
|
||||
endTime Int @default(1440)
|
||||
startTime Int @default(0)
|
||||
endTime Int @default(1440)
|
||||
// </DEPRECATED>
|
||||
bufferTime Int @default(0)
|
||||
hideBranding Boolean @default(false)
|
||||
theme String?
|
||||
createdDate DateTime @default(now()) @map(name: "created")
|
||||
trialEndsAt DateTime?
|
||||
eventTypes EventType[] @relation("user_eventtype")
|
||||
credentials Credential[]
|
||||
teams Membership[]
|
||||
bookings Booking[]
|
||||
schedules Schedule[]
|
||||
defaultScheduleId Int?
|
||||
selectedCalendars SelectedCalendar[]
|
||||
completedOnboarding Boolean @default(false)
|
||||
locale String?
|
||||
timeFormat Int? @default(12)
|
||||
twoFactorSecret String?
|
||||
twoFactorEnabled Boolean @default(false)
|
||||
identityProvider IdentityProvider @default(CAL)
|
||||
identityProviderId String?
|
||||
availability Availability[]
|
||||
invitedTo Int?
|
||||
plan UserPlan @default(TRIAL)
|
||||
webhooks Webhook[]
|
||||
brandColor String @default("#292929")
|
||||
darkBrandColor String @default("#fafafa")
|
||||
bufferTime Int @default(0)
|
||||
hideBranding Boolean @default(false)
|
||||
theme String?
|
||||
createdDate DateTime @default(now()) @map(name: "created")
|
||||
trialEndsAt DateTime?
|
||||
eventTypes EventType[] @relation("user_eventtype")
|
||||
credentials Credential[]
|
||||
teams Membership[]
|
||||
bookings Booking[]
|
||||
schedules Schedule[]
|
||||
defaultScheduleId Int?
|
||||
selectedCalendars SelectedCalendar[]
|
||||
completedOnboarding Boolean @default(false)
|
||||
locale String?
|
||||
timeFormat Int? @default(12)
|
||||
twoFactorSecret String?
|
||||
twoFactorEnabled Boolean @default(false)
|
||||
identityProvider IdentityProvider @default(CAL)
|
||||
identityProviderId String?
|
||||
availability Availability[]
|
||||
invitedTo Int?
|
||||
plan UserPlan @default(TRIAL)
|
||||
webhooks Webhook[]
|
||||
brandColor String @default("#292929")
|
||||
darkBrandColor String @default("#fafafa")
|
||||
// the location where the events will end up
|
||||
destinationCalendar DestinationCalendar?
|
||||
away Boolean @default(false)
|
||||
destinationCalendar DestinationCalendar?
|
||||
away Boolean @default(false)
|
||||
// participate in dynamic group booking or not
|
||||
allowDynamicBooking Boolean? @default(true)
|
||||
metadata Json?
|
||||
verified Boolean? @default(false)
|
||||
role UserPermissionRole @default(USER)
|
||||
impersonatedUsers Impersonations[] @relation("impersonated_user")
|
||||
impersonatedBy Impersonations[] @relation("impersonated_by_user")
|
||||
apiKeys ApiKey[]
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
allowDynamicBooking Boolean? @default(true)
|
||||
metadata Json?
|
||||
verified Boolean? @default(false)
|
||||
role UserPermissionRole @default(USER)
|
||||
disableImpersonation Boolean @default(false)
|
||||
impersonatedUsers Impersonations[] @relation("impersonated_user")
|
||||
impersonatedBy Impersonations[] @relation("impersonated_by_user")
|
||||
apiKeys ApiKey[]
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
|
||||
Feedback Feedback[]
|
||||
@@map(name: "users")
|
||||
|
|
Loading…
Reference in New Issue
Block a user