chore: rename DWD to DelegationCredential (#19703)

* chore: rename DWD to DelegationCredential

* fixup! chore: rename DWD to DelegationCredential

* fixup! fixup! chore: rename DWD to DelegationCredential

* fixup! Merge branch 'main' into rename-domain-wide-delegation

* fixup! fixup! Merge branch 'main' into rename-domain-wide-delegation

* fixup! fixup! fixup! Merge branch 'main' into rename-domain-wide-delegation
This commit is contained in:
Morgan
2025-03-04 09:45:39 -03:00
committed by GitHub
parent a39b5e47a7
commit 7a9ddf2194
167 changed files with 4232 additions and 2356 deletions
@@ -2,7 +2,7 @@ import type { Prisma } from "@prisma/client";
import type { NextApiRequest } from "next";
import type { z } from "zod";
import { getCalendarCredentialsWithoutDwd, getConnectedCalendars } from "@calcom/lib/CalendarManager";
import { getCalendarCredentialsWithoutDelegation, getConnectedCalendars } from "@calcom/lib/CalendarManager";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
@@ -185,7 +185,7 @@ async function verifyCredentialsAndGetId({
currentCredentialId: number | null;
}) {
if (parsedBody.integration && parsedBody.externalId) {
const calendarCredentials = getCalendarCredentialsWithoutDwd(userCredentials);
const calendarCredentials = getCalendarCredentialsWithoutDelegation(userCredentials);
const { connectedCalendars } = await getConnectedCalendars(
calendarCredentials,
@@ -1,6 +1,6 @@
import type { NextApiRequest } from "next";
import { getCalendarCredentialsWithoutDwd, getConnectedCalendars } from "@calcom/lib/CalendarManager";
import { getCalendarCredentialsWithoutDelegation, getConnectedCalendars } from "@calcom/lib/CalendarManager";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
@@ -82,7 +82,7 @@ async function postHandler(req: NextApiRequest) {
message: "Bad request, credential id invalid",
});
const calendarCredentials = getCalendarCredentialsWithoutDwd(userCredentials);
const calendarCredentials = getCalendarCredentialsWithoutDelegation(userCredentials);
const { connectedCalendars } = await getConnectedCalendars(calendarCredentials, [], parsedBody.externalId);
@@ -81,7 +81,7 @@ describe("POST /api/selected-calendars", () => {
userId: 444444,
id: "xxx-xxx",
eventTypeId: null,
domainWideDelegationCredentialId: null,
delegationCredentialId: null,
googleChannelId: null,
googleChannelKind: null,
googleChannelResourceId: null,
@@ -123,7 +123,7 @@ describe("POST /api/selected-calendars", () => {
googleChannelResourceId: null,
googleChannelResourceUri: null,
googleChannelExpiration: null,
domainWideDelegationCredentialId: null,
delegationCredentialId: null,
eventTypeId: null,
error: null,
});
+1 -1
View File
@@ -30,7 +30,7 @@
"@axiomhq/winston": "^1.2.0",
"@calcom/platform-constants": "*",
"@calcom/platform-enums": "*",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.124",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.126",
"@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2",
"@calcom/platform-types": "*",
"@calcom/platform-utils": "*",
@@ -131,7 +131,7 @@ class Primary {
@IsString()
@IsOptional()
@ApiPropertyOptional({ type: String, nullable: true })
domainWideDelegationCredentialId?: string | null;
delegationCredentialId?: string | null;
}
export class Calendar {
@@ -174,7 +174,7 @@ export class Calendar {
@IsString()
@IsOptional()
@ApiPropertyOptional({ type: String, nullable: true })
domainWideDelegationCredentialId?: string | null;
delegationCredentialId?: string | null;
}
export class ConnectedCalendar {
@@ -190,7 +190,7 @@ export class ConnectedCalendar {
@IsString()
@IsOptional()
@ApiPropertyOptional({ type: String, nullable: true })
domainWideDelegationCredentialId?: string | null;
delegationCredentialId?: string | null;
@ValidateNested()
@IsObject()
@@ -238,7 +238,7 @@ class DestinationCalendar {
@IsString()
@IsOptional()
@ApiPropertyOptional({ type: String, nullable: true })
domainWideDelegationCredentialId?: string | null;
delegationCredentialId?: string | null;
@IsString()
@IsOptional()
@@ -39,7 +39,7 @@ export class CalendarsService {
private readonly selectedCalendarsRepository: SelectedCalendarsRepository
) {}
private buildNonDwdCredentials<TCredential>(credentials: TCredential[]) {
private buildNonDelegationCredentials<TCredential>(credentials: TCredential[]) {
return credentials
.map((credential) => ({
...credential,
@@ -83,7 +83,7 @@ export class CalendarsService {
);
try {
const calendarBusyTimes = await getBusyCalendarTimes(
this.buildNonDwdCredentials(credentials),
this.buildNonDelegationCredentials(credentials),
dateFrom,
dateTo,
composedSelectedCalendars
@@ -210,7 +210,7 @@ export class EventTypesAtomService {
credentials = credentials.concat(teamAppCredentials);
}
}
//TODO: enrich credentials for DWD
//TODO: enrich credentials for DelegationCredential
const enabledApps = await getEnabledAppsFromCredentials(credentials, {
where: { slug },
});
@@ -29,12 +29,12 @@ export class DestinationCalendarsController {
@Body() input: DestinationCalendarsInputBodyDto,
@GetUser() user: UserWithProfile
): Promise<DestinationCalendarsOutputResponseDto> {
const { integration, externalId, domainWideDelegationCredentialId } = input;
const { integration, externalId, delegationCredentialId } = input;
const updatedDestinationCalendar = await this.destinationCalendarsService.updateDestinationCalendars(
integration,
externalId,
user.id,
domainWideDelegationCredentialId
delegationCredentialId
);
return {
@@ -12,21 +12,21 @@ export class DestinationCalendarsRepository {
userId: number,
primaryEmail: string | null,
credentialId?: number,
domainWideDelegationCredentialId?: string
delegationCredentialId?: string
) {
return await this.dbWrite.prisma.destinationCalendar.upsert({
update: {
integration,
externalId,
...(credentialId ? { credentialId } : {}),
...(domainWideDelegationCredentialId ? { domainWideDelegationCredentialId } : {}),
...(delegationCredentialId ? { delegationCredentialId } : {}),
primaryEmail,
},
create: {
integration,
externalId,
...(credentialId ? { credentialId } : {}),
...(domainWideDelegationCredentialId ? { domainWideDelegationCredentialId } : {}),
...(delegationCredentialId ? { delegationCredentialId } : {}),
primaryEmail,
userId,
},
@@ -37,5 +37,5 @@ export class DestinationCalendarsInputBodyDto {
@IsString()
@IsOptional()
@ApiPropertyOptional()
readonly domainWideDelegationCredentialId?: string;
readonly delegationCredentialId?: string;
}
@@ -14,10 +14,10 @@ export class DestinationCalendarsService {
integration: string,
externalId: string,
userId: number,
domainWideDelegationCredentialId?: string
delegationCredentialId?: string
) {
// note(Lauris): todo remove the log but leaving this now to confirm domainWideDelegationCredentialId is received
console.log("debug: domainWideDelegationCredentialId", domainWideDelegationCredentialId);
// note(Lauris): todo remove the log but leaving this now to confirm delegationCredentialId is received
console.log("debug: delegationCredentialId", delegationCredentialId);
const userCalendars = await this.calendarsService.getCalendars(userId);
const allCalendars: Calendar[] = userCalendars.connectedCalendars
.map((cal: ConnectedCalendar) => cal.calendars ?? [])
@@ -27,25 +27,25 @@ export class DestinationCalendarsService {
cal.externalId === externalId && cal.integration === integration && cal.readOnly === false
)?.credentialId;
if (!domainWideDelegationCredentialId && !credentialId) {
if (!delegationCredentialId && !credentialId) {
throw new NotFoundException(`Could not find calendar ${externalId}`);
}
const dwdCalendar = domainWideDelegationCredentialId
const delegatedCalendar = delegationCredentialId
? allCalendars.find(
(cal: Calendar) =>
cal.externalId === externalId &&
cal.integration === integration &&
cal.domainWideDelegationCredentialId === domainWideDelegationCredentialId
cal.delegationCredentialId === delegationCredentialId
)
: undefined;
if (domainWideDelegationCredentialId && !dwdCalendar) {
if (delegationCredentialId && !delegatedCalendar) {
throw new NotFoundException(`Could not find calendar ${externalId}`);
}
const primaryEmail = dwdCalendar
? (dwdCalendar.primary && dwdCalendar?.email) || undefined
const primaryEmail = delegatedCalendar
? (delegatedCalendar.primary && delegatedCalendar?.email) || undefined
: allCalendars.find((cal: Calendar) => cal.primary && cal.credentialId === credentialId)?.email;
const {
@@ -58,8 +58,8 @@ export class DestinationCalendarsService {
externalId,
userId,
primaryEmail ?? null,
dwdCalendar ? undefined : credentialId,
dwdCalendar ? domainWideDelegationCredentialId : undefined
delegatedCalendar ? undefined : credentialId,
delegatedCalendar ? delegationCredentialId : undefined
);
return {
@@ -2,12 +2,12 @@ import {
GoogleServiceAccountKeyInput,
ServiceAccountKeyValidator,
MicrosoftServiceAccountKeyInput,
} from "@/modules/organizations/dwd/inputs/service-account-key.input";
} from "@/modules/organizations/delegation-credentials/inputs/service-account-key.input";
import { ApiProperty, getSchemaPath } from "@nestjs/swagger";
import { Expose, Type } from "class-transformer";
import { IsString, IsNotEmpty, ValidateNested, Validate } from "class-validator";
export class CreateDwdInput {
export class CreateDelegationCredentialInput {
@IsString()
@IsNotEmpty()
@ApiProperty()
@@ -2,12 +2,12 @@ import {
GoogleServiceAccountKeyInput,
MicrosoftServiceAccountKeyInput,
ServiceAccountKeyValidator,
} from "@/modules/organizations/dwd/inputs/service-account-key.input";
} from "@/modules/organizations/delegation-credentials/inputs/service-account-key.input";
import { ApiPropertyOptional, getSchemaPath } from "@nestjs/swagger";
import { Expose, plainToClass, Transform, Type } from "class-transformer";
import { IsBoolean, IsOptional, Validate, ValidateNested } from "class-validator";
export class UpdateDwdInput {
export class UpdateDelegationCredentialInput {
@IsBoolean()
@IsOptional()
@ApiPropertyOptional()
@@ -7,11 +7,11 @@ import { PlatformPlanGuard } from "@/modules/auth/guards/billing/platform-plan.g
import { IsAdminAPIEnabledGuard } from "@/modules/auth/guards/organizations/is-admin-api-enabled.guard";
import { IsOrgGuard } from "@/modules/auth/guards/organizations/is-org.guard";
import { RolesGuard } from "@/modules/auth/guards/roles/roles.guard";
import { UpdateDwdInput } from "@/modules/organizations/dwd/inputs/update-dwd.input";
import { CreateDwdOutput } from "@/modules/organizations/dwd/outputs/create-dwd.output";
import { DwdOutput } from "@/modules/organizations/dwd/outputs/dwd.output";
import { UpdateDwdOutput } from "@/modules/organizations/dwd/outputs/update-dwd.output";
import { OrganizationsDwdService } from "@/modules/organizations/dwd/services/organizations-dwd.service";
import { UpdateDelegationCredentialInput } from "@/modules/organizations/delegation-credentials/inputs/update-delegation-credential.input";
import { CreateDelegationCredentialOutput } from "@/modules/organizations/delegation-credentials/outputs/create-delegation-credential.output";
import { DelegationCredentialOutput } from "@/modules/organizations/delegation-credentials/outputs/delegation-credential.output";
import { UpdateDelegationCredentialOutput } from "@/modules/organizations/delegation-credentials/outputs/update-delegation-credential.output";
import { OrganizationsDelegationCredentialService } from "@/modules/organizations/delegation-credentials/services/organizations-delegation-credential.service";
import {
Controller,
UseGuards,
@@ -29,7 +29,7 @@ import { plainToClass } from "class-transformer";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
import { CreateDwdInput } from "./inputs/create-dwd.input";
import { CreateDelegationCredentialInput } from "./inputs/create-delegation-credential.input";
@Controller({
path: "/v2/organizations/:orgId/delegation-credentials",
@@ -37,23 +37,27 @@ import { CreateDwdInput } from "./inputs/create-dwd.input";
})
@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard)
@DocsTags("Orgs / Delegation Credentials")
export class OrganizationsDwdController {
constructor(private readonly dwdService: OrganizationsDwdService) {}
export class OrganizationsDelegationCredentialController {
constructor(private readonly delegationCredentialService: OrganizationsDelegationCredentialService) {}
@Post("/")
@HttpCode(HttpStatus.CREATED)
@Roles("ORG_ADMIN")
@PlatformPlan("SCALE")
@ApiOperation({ summary: "Save delegation credentials for your organization." })
async createDwd(
async createDelegationCredential(
@Param("orgId", ParseIntPipe) orgId: number,
@GetUser() dwdServiceAccountUser: User,
@Body() body: CreateDwdInput
): Promise<CreateDwdOutput> {
const dwd = await this.dwdService.createDwd(orgId, dwdServiceAccountUser, body);
@GetUser() delegatedServiceAccountUser: User,
@Body() body: CreateDelegationCredentialInput
): Promise<CreateDelegationCredentialOutput> {
const delegationCredential = await this.delegationCredentialService.createDelegationCredential(
orgId,
delegatedServiceAccountUser,
body
);
return {
status: SUCCESS_STATUS,
data: plainToClass(DwdOutput, dwd, { strategy: "excludeAll" }),
data: plainToClass(DelegationCredentialOutput, delegationCredential, { strategy: "excludeAll" }),
};
}
@@ -61,16 +65,21 @@ export class OrganizationsDwdController {
@Roles("ORG_ADMIN")
@PlatformPlan("SCALE")
@ApiOperation({ summary: "Update delegation credentials of your organization." })
async updateDwd(
async updateDelegationCredential(
@Param("orgId", ParseIntPipe) orgId: number,
@GetUser() dwdServiceAccountUser: User,
@Body() body: UpdateDwdInput,
@GetUser() delegatedServiceAccountUser: User,
@Body() body: UpdateDelegationCredentialInput,
@Param("credentialId") credentialId: string
): Promise<UpdateDwdOutput> {
const dwd = await this.dwdService.updateDwd(orgId, credentialId, dwdServiceAccountUser, body);
): Promise<UpdateDelegationCredentialOutput> {
const delegationCredential = await this.delegationCredentialService.updateDelegationCredential(
orgId,
credentialId,
delegatedServiceAccountUser,
body
);
return {
status: SUCCESS_STATUS,
data: plainToClass(DwdOutput, dwd, { strategy: "excludeAll" }),
data: plainToClass(DelegationCredentialOutput, delegationCredential, { strategy: "excludeAll" }),
};
}
}
@@ -0,0 +1,21 @@
import { MembershipsModule } from "@/modules/memberships/memberships.module";
import { OrganizationsDelegationCredentialController } from "@/modules/organizations/delegation-credentials/organizations-delegation-credential.controller";
import { OrganizationsDelegationCredentialRepository } from "@/modules/organizations/delegation-credentials/organizations-delegation-credential.repository";
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { RedisModule } from "@/modules/redis/redis.module";
import { StripeModule } from "@/modules/stripe/stripe.module";
import { Module } from "@nestjs/common";
import { OrganizationsDelegationCredentialService } from "./services/organizations-delegation-credential.service";
@Module({
imports: [PrismaModule, StripeModule, RedisModule, MembershipsModule],
providers: [
OrganizationsDelegationCredentialService,
OrganizationsDelegationCredentialRepository,
OrganizationsRepository,
],
controllers: [OrganizationsDelegationCredentialController],
})
export class OrganizationsDelegationCredentialModule {}
@@ -0,0 +1,31 @@
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { Injectable } from "@nestjs/common";
import { Prisma } from "@calcom/prisma/client";
@Injectable()
export class OrganizationsDelegationCredentialRepository {
constructor(private readonly dbRead: PrismaReadService) {}
async findById(delegationCredentialId: string) {
return this.dbRead.prisma.delegationCredential.findUnique({ where: { id: delegationCredentialId } });
}
async findByIdWithWorkspacePlatform(delegationCredentialId: string) {
return this.dbRead.prisma.delegationCredential.findUnique({
where: { id: delegationCredentialId },
include: { workspacePlatform: true },
});
}
async updateIncludeWorkspacePlatform(
delegationCredentialId: string,
data: Prisma.DelegationCredentialUncheckedUpdateInput
) {
return this.dbRead.prisma.delegationCredential.update({
where: { id: delegationCredentialId },
data,
include: { workspacePlatform: true },
});
}
}
@@ -1,20 +1,20 @@
import { DwdOutput } from "@/modules/organizations/dwd/outputs/dwd.output";
import { DelegationCredentialOutput } from "@/modules/organizations/delegation-credentials/outputs/delegation-credential.output";
import { ApiProperty } from "@nestjs/swagger";
import { Type } from "class-transformer";
import { IsEnum, IsNotEmptyObject, ValidateNested } from "class-validator";
import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
export class UpdateDwdOutput {
export class CreateDelegationCredentialOutput {
@ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] })
@IsEnum([SUCCESS_STATUS, ERROR_STATUS])
status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS;
@ApiProperty({
type: DwdOutput,
type: DelegationCredentialOutput,
})
@IsNotEmptyObject()
@ValidateNested()
@Type(() => DwdOutput)
data!: DwdOutput;
@Type(() => DelegationCredentialOutput)
data!: DelegationCredentialOutput;
}
@@ -14,7 +14,7 @@ class WorkspacePlatformDto {
slug!: string;
}
export class DwdOutput {
export class DelegationCredentialOutput {
@ApiProperty()
@IsString()
@Expose()
@@ -1,20 +1,20 @@
import { DwdOutput } from "@/modules/organizations/dwd/outputs/dwd.output";
import { DelegationCredentialOutput } from "@/modules/organizations/delegation-credentials/outputs/delegation-credential.output";
import { ApiProperty } from "@nestjs/swagger";
import { Type } from "class-transformer";
import { IsEnum, IsNotEmptyObject, ValidateNested } from "class-validator";
import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
export class CreateDwdOutput {
export class UpdateDelegationCredentialOutput {
@ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] })
@IsEnum([SUCCESS_STATUS, ERROR_STATUS])
status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS;
@ApiProperty({
type: DwdOutput,
type: DelegationCredentialOutput,
})
@IsNotEmptyObject()
@ValidateNested()
@Type(() => DwdOutput)
data!: DwdOutput;
@Type(() => DelegationCredentialOutput)
data!: DelegationCredentialOutput;
}
@@ -0,0 +1,93 @@
import { CreateDelegationCredentialInput } from "@/modules/organizations/delegation-credentials/inputs/create-delegation-credential.input";
import {
GoogleServiceAccountKeyInput,
MicrosoftServiceAccountKeyInput,
} from "@/modules/organizations/delegation-credentials/inputs/service-account-key.input";
import { UpdateDelegationCredentialInput } from "@/modules/organizations/delegation-credentials/inputs/update-delegation-credential.input";
import { OrganizationsDelegationCredentialRepository } from "@/modules/organizations/delegation-credentials/organizations-delegation-credential.repository";
import { DelegationCredentialOutput } from "@/modules/organizations/delegation-credentials/outputs/delegation-credential.output";
import { Injectable, NotFoundException } from "@nestjs/common";
import { User } from "@prisma/client";
import {
addDelegationCredential,
toggleDelegationCredentialEnabled,
encryptServiceAccountKey,
} from "@calcom/platform-libraries";
@Injectable()
export class OrganizationsDelegationCredentialService {
constructor(
private readonly organizationsDelegationCredentialRepository: OrganizationsDelegationCredentialRepository
) {}
async createDelegationCredential(
orgId: number,
delegatedServiceAccountUser: User,
body: CreateDelegationCredentialInput
) {
const delegationCredential = await addDelegationCredential({
input: body,
ctx: { user: { id: delegatedServiceAccountUser.id, organizationId: orgId } },
});
return delegationCredential;
}
async updateDelegationCredential(
orgId: number,
delegationCredentialId: string,
delegatedServiceAccountUser: User,
body: UpdateDelegationCredentialInput
) {
if (body.enabled !== undefined) {
await this.updateDelegationCredentialEnabled(
orgId,
delegationCredentialId,
delegatedServiceAccountUser,
body.enabled
);
}
if (body.serviceAccountKey !== undefined) {
await this.updateDelegationCredentialServiceAccountKey(delegationCredentialId, body.serviceAccountKey);
}
const delegationCredential =
await this.organizationsDelegationCredentialRepository.findByIdWithWorkspacePlatform(
delegationCredentialId
);
if (!delegationCredential) {
throw new NotFoundException(`DelegationCredential with id ${delegationCredentialId} not found`);
}
return delegationCredential;
}
async updateDelegationCredentialEnabled(
orgId: number,
delegationCredentialId: string,
delegatedServiceAccountUser: User,
enabled: boolean
) {
const handlerUser = {
id: delegatedServiceAccountUser.id,
email: delegatedServiceAccountUser.email,
organizationId: orgId,
};
const handlerBody = { id: delegationCredentialId, enabled };
const delegationCredential = await toggleDelegationCredentialEnabled(handlerUser, handlerBody);
return delegationCredential;
}
async updateDelegationCredentialServiceAccountKey(
delegationCredentialId: string,
serviceAccountKey: GoogleServiceAccountKeyInput | MicrosoftServiceAccountKeyInput
) {
const encryptedServiceAccountKey = encryptServiceAccountKey(serviceAccountKey);
const delegationCredential =
await this.organizationsDelegationCredentialRepository.updateIncludeWorkspacePlatform(
delegationCredentialId,
{
serviceAccountKey: encryptedServiceAccountKey,
}
);
return delegationCredential;
}
}
@@ -1,17 +0,0 @@
import { MembershipsModule } from "@/modules/memberships/memberships.module";
import { OrganizationsDwdController } from "@/modules/organizations/dwd/organizations-dwd.controller";
import { OrganizationsDwdRepository } from "@/modules/organizations/dwd/organizations-dwd.repository";
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { RedisModule } from "@/modules/redis/redis.module";
import { StripeModule } from "@/modules/stripe/stripe.module";
import { Module } from "@nestjs/common";
import { OrganizationsDwdService } from "./services/organizations-dwd.service";
@Module({
imports: [PrismaModule, StripeModule, RedisModule, MembershipsModule],
providers: [OrganizationsDwdService, OrganizationsDwdRepository, OrganizationsRepository],
controllers: [OrganizationsDwdController],
})
export class OrganizationsDwdModule {}
@@ -1,28 +0,0 @@
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { Injectable } from "@nestjs/common";
import { Prisma } from "@calcom/prisma/client";
@Injectable()
export class OrganizationsDwdRepository {
constructor(private readonly dbRead: PrismaReadService) {}
async findById(dwdId: string) {
return this.dbRead.prisma.domainWideDelegation.findUnique({ where: { id: dwdId } });
}
async findByIdWithWorkspacePlatform(dwdId: string) {
return this.dbRead.prisma.domainWideDelegation.findUnique({
where: { id: dwdId },
include: { workspacePlatform: true },
});
}
async updateIncludeWorkspacePlatform(dwdId: string, data: Prisma.DomainWideDelegationUncheckedUpdateInput) {
return this.dbRead.prisma.domainWideDelegation.update({
where: { id: dwdId },
data,
include: { workspacePlatform: true },
});
}
}
@@ -1,62 +0,0 @@
import { CreateDwdInput } from "@/modules/organizations/dwd/inputs/create-dwd.input";
import {
GoogleServiceAccountKeyInput,
MicrosoftServiceAccountKeyInput,
} from "@/modules/organizations/dwd/inputs/service-account-key.input";
import { UpdateDwdInput } from "@/modules/organizations/dwd/inputs/update-dwd.input";
import { OrganizationsDwdRepository } from "@/modules/organizations/dwd/organizations-dwd.repository";
import { DwdOutput } from "@/modules/organizations/dwd/outputs/dwd.output";
import { Injectable, NotFoundException } from "@nestjs/common";
import { User } from "@prisma/client";
import { addDwd, toggleDwdEnabled, encryptServiceAccountKey } from "@calcom/platform-libraries";
@Injectable()
export class OrganizationsDwdService {
constructor(private readonly organizationsDwdRepository: OrganizationsDwdRepository) {}
async createDwd(orgId: number, dwdServiceAccountUser: User, body: CreateDwdInput) {
console.log("asap body", JSON.stringify(body, null, 2));
const dwd = await addDwd({
input: body,
ctx: { user: { id: dwdServiceAccountUser.id, organizationId: orgId } },
});
return dwd;
}
async updateDwd(orgId: number, dwdId: string, dwdServiceAccountUser: User, body: UpdateDwdInput) {
if (body.enabled !== undefined) {
await this.updateDwdEnabled(orgId, dwdId, dwdServiceAccountUser, body.enabled);
}
if (body.serviceAccountKey !== undefined) {
await this.updateDwdServiceAccountKey(dwdId, body.serviceAccountKey);
}
const dwd = await this.organizationsDwdRepository.findByIdWithWorkspacePlatform(dwdId);
if (!dwd) {
throw new NotFoundException(`DWD with id ${dwdId} not found`);
}
return dwd;
}
async updateDwdEnabled(orgId: number, dwdId: string, dwdServiceAccountUser: User, enabled: boolean) {
const handlerUser = {
id: dwdServiceAccountUser.id,
email: dwdServiceAccountUser.email,
organizationId: orgId,
};
const handlerBody = { id: dwdId, enabled };
const dwd = await toggleDwdEnabled(handlerUser, handlerBody);
return dwd;
}
async updateDwdServiceAccountKey(
dwdId: string,
serviceAccountKey: GoogleServiceAccountKeyInput | MicrosoftServiceAccountKeyInput
) {
const encryptedServiceAccountKey = encryptServiceAccountKey(serviceAccountKey);
const dwd = await this.organizationsDwdRepository.updateIncludeWorkspacePlatform(dwdId, {
serviceAccountKey: encryptedServiceAccountKey,
});
return dwd;
}
}
@@ -11,7 +11,7 @@ import { OrganizationAttributesService } from "@/modules/organizations/attribute
import { OrganizationAttributeOptionRepository } from "@/modules/organizations/attributes/options/organization-attribute-options.repository";
import { OrganizationsAttributesOptionsController } from "@/modules/organizations/attributes/options/organizations-attributes-options.controller";
import { OrganizationAttributeOptionService } from "@/modules/organizations/attributes/options/services/organization-attributes-option.service";
import { OrganizationsDwdModule } from "@/modules/organizations/dwd/organizations-dwd.module";
import { OrganizationsDelegationCredentialModule } from "@/modules/organizations/delegation-credentials/organizations-delegation-credential.module";
import { OrganizationsEventTypesController } from "@/modules/organizations/event-types/organizations-event-types.controller";
import { OrganizationsEventTypesRepository } from "@/modules/organizations/event-types/organizations-event-types.repository";
import { OutputTeamEventTypesResponsePipe } from "@/modules/organizations/event-types/pipes/team-event-types-response.transformer";
@@ -65,7 +65,7 @@ import { Module } from "@nestjs/common";
EventTypesModule_2024_06_14,
TeamsEventTypesModule,
TeamsModule,
OrganizationsDwdModule,
OrganizationsDelegationCredentialModule,
OrganizationsOrganizationsModule,
OrganizationsTeamsRoutingFormsModule,
],
@@ -12,7 +12,7 @@ export class SelectedCalendarsInputDto {
@IsString()
@IsOptional()
readonly domainWideDelegationCredentialId?: string;
readonly delegationCredentialId?: string;
}
export class SelectedCalendarsQueryParamsInputDto {
@@ -27,5 +27,5 @@ export class SelectedCalendarsQueryParamsInputDto {
@IsString()
@IsOptional()
readonly domainWideDelegationCredentialId?: string;
readonly delegationCredentialId?: string;
}
@@ -2,7 +2,7 @@ import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
import { AppsRepository } from "@/modules/apps/apps.repository";
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
import { OrganizationsDwdRepository } from "@/modules/organizations/dwd/organizations-dwd.repository";
import { OrganizationsDelegationCredentialRepository } from "@/modules/organizations/delegation-credentials/organizations-delegation-credential.repository";
import { OrganizationsMembershipRepository } from "@/modules/organizations/memberships/organizations-membership.repository";
import { OrganizationsMembershipService } from "@/modules/organizations/memberships/services/organizations-membership.service";
import { PrismaModule } from "@/modules/prisma/prisma.module";
@@ -22,7 +22,7 @@ import { Module } from "@nestjs/common";
CredentialsRepository,
AppsRepository,
OrganizationsMembershipService,
OrganizationsDwdRepository,
OrganizationsDelegationCredentialRepository,
OrganizationsMembershipRepository,
SelectedCalendarsService,
],
@@ -89,7 +89,7 @@ export class SelectedCalendarsRepository {
userId: number,
integration: string,
externalId: string,
domainWideDelegationCredentialId?: string
delegationCredentialId?: string
) {
// Using deleteMany because userId_externalId_integration_eventTypeId is a unique constraint but with eventTypeId being nullable, causing it to be not used as a unique constraint
const records = await this.dbWrite.prisma.selectedCalendar.findMany({
@@ -97,7 +97,7 @@ export class SelectedCalendarsRepository {
userId,
externalId,
integration,
domainWideDelegationCredentialId,
delegationCredentialId,
...ensureUserLevelWhere,
},
});
@@ -1,5 +1,5 @@
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
import { OrganizationsDwdRepository } from "@/modules/organizations/dwd/organizations-dwd.repository";
import { OrganizationsDelegationCredentialRepository } from "@/modules/organizations/delegation-credentials/organizations-delegation-credential.repository";
import { OrganizationsMembershipService } from "@/modules/organizations/memberships/services/organizations-membership.service";
import {
SelectedCalendarsInputDto,
@@ -11,7 +11,9 @@ import { Injectable, NotFoundException } from "@nestjs/common";
import { SelectedCalendarRepository } from "@calcom/platform-libraries";
type SelectedCalendarsInputDwd = SelectedCalendarsInputDto & { domainWideDelegationCredentialId: string };
type SelectedCalendarsInputDelegationCredential = SelectedCalendarsInputDto & {
delegationCredentialId: string;
};
@Injectable()
export class SelectedCalendarsService {
@@ -19,16 +21,16 @@ export class SelectedCalendarsService {
private readonly calendarsService: CalendarsService,
private readonly selectedCalendarsRepository: SelectedCalendarsRepository,
private readonly organizationsMembershipService: OrganizationsMembershipService,
private readonly organizationsDwdRepository: OrganizationsDwdRepository
private readonly organizationsDelegationCredentialRepository: OrganizationsDelegationCredentialRepository
) {}
async addSelectedCalendar(user: UserWithProfile, input: SelectedCalendarsInputDto) {
if (input.domainWideDelegationCredentialId) {
const dwdInput = {
if (input.delegationCredentialId) {
const delegationCredentialInput = {
...input,
domainWideDelegationCredentialId: input.domainWideDelegationCredentialId,
delegationCredentialId: input.delegationCredentialId,
};
return this.addSelectedCalendarDwd(user, dwdInput);
return this.addSelectedCalendarDelegationCredential(user, delegationCredentialInput);
}
return this.addSelectedCalendarUser(user, input);
}
@@ -47,35 +49,42 @@ export class SelectedCalendarsService {
return userSelectedCalendar;
}
private async addSelectedCalendarDwd(user: UserWithProfile, selectedCalendar: SelectedCalendarsInputDwd) {
const isMemberOfOrganization = await this.isMemberOfDwdOrganization(
private async addSelectedCalendarDelegationCredential(
user: UserWithProfile,
selectedCalendar: SelectedCalendarsInputDelegationCredential
) {
const isMemberOfOrganization = await this.isMemberOfDelegationCredentialOrganization(
user.id,
selectedCalendar.domainWideDelegationCredentialId
selectedCalendar.delegationCredentialId
);
if (!isMemberOfOrganization) {
throw new NotFoundException("User is not a member of the organization that owns the DWD credential");
throw new NotFoundException(
"User is not a member of the organization that owns the Delegation credential"
);
}
const { integration, externalId, credentialId, domainWideDelegationCredentialId } = selectedCalendar;
const dwdSelectedCalendar = await SelectedCalendarRepository.upsert({
const { integration, externalId, credentialId, delegationCredentialId } = selectedCalendar;
const delegationCredentialSelectedCalendar = await SelectedCalendarRepository.upsert({
userId: user.id,
integration,
externalId,
credentialId,
domainWideDelegationCredentialId,
delegationCredentialId,
eventTypeId: null,
});
return dwdSelectedCalendar;
return delegationCredentialSelectedCalendar;
}
private async isMemberOfDwdOrganization(userId: number, domainWideDelegationCredentialId: string) {
const dwd = await this.organizationsDwdRepository.findById(domainWideDelegationCredentialId);
if (!dwd) {
throw new NotFoundException("DWD with provided domainWideDelegationCredentialId not found");
private async isMemberOfDelegationCredentialOrganization(userId: number, delegationCredentialId: string) {
const delegationCredential = await this.organizationsDelegationCredentialRepository.findById(
delegationCredentialId
);
if (!delegationCredential) {
throw new NotFoundException("DelegationCredential with provided delegationCredentialId not found");
}
const isMemberOfOrganization = await this.organizationsMembershipService.getOrgMembershipByUserId(
dwd.organizationId,
delegationCredential.organizationId,
userId
);
@@ -86,17 +95,19 @@ export class SelectedCalendarsService {
selectedCalendar: SelectedCalendarsQueryParamsInputDto,
user: UserWithProfile
) {
const { integration, externalId, credentialId, domainWideDelegationCredentialId } = selectedCalendar;
const { integration, externalId, credentialId, delegationCredentialId } = selectedCalendar;
if (!domainWideDelegationCredentialId) {
if (!delegationCredentialId) {
await this.calendarsService.checkCalendarCredentials(Number(credentialId), user.id);
} else {
const isMemberOfOrganization = await this.isMemberOfDwdOrganization(
const isMemberOfOrganization = await this.isMemberOfDelegationCredentialOrganization(
user.id,
domainWideDelegationCredentialId
delegationCredentialId
);
if (!isMemberOfOrganization) {
throw new NotFoundException("User is not a member of the organization that owns the DWD credential");
throw new NotFoundException(
"User is not a member of the organization that owns the Delegation credential"
);
}
}
@@ -104,7 +115,7 @@ export class SelectedCalendarsService {
user.id,
integration,
externalId,
domainWideDelegationCredentialId
delegationCredentialId
);
return removedCalendarEntry;
+20 -20
View File
@@ -1093,7 +1093,7 @@
},
"/v2/organizations/{orgId}/delegation-credentials": {
"post": {
"operationId": "OrganizationsDwdController_createDwd",
"operationId": "OrganizationsDelegationCredentialController_createDelegationCredential",
"summary": "Save delegation credentials for your organization.",
"parameters": [
{
@@ -1110,7 +1110,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateDwdInput"
"$ref": "#/components/schemas/CreateDelegationCredentialInput"
}
}
}
@@ -1121,7 +1121,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateDwdOutput"
"$ref": "#/components/schemas/CreateDelegationCredentialOutput"
}
}
}
@@ -1134,7 +1134,7 @@
},
"/v2/organizations/{orgId}/delegation-credentials/{credentialId}": {
"patch": {
"operationId": "OrganizationsDwdController_updateDwd",
"operationId": "OrganizationsDelegationCredentialController_updateDelegationCredential",
"summary": "Update delegation credentials of your organization.",
"parameters": [
{
@@ -1159,7 +1159,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateDwdInput"
"$ref": "#/components/schemas/UpdateDelegationCredentialInput"
}
}
}
@@ -1170,7 +1170,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateDwdOutput"
"$ref": "#/components/schemas/UpdateDelegationCredentialOutput"
}
}
}
@@ -5942,7 +5942,7 @@
}
},
{
"name": "domainWideDelegationCredentialId",
"name": "delegationCredentialId",
"required": false,
"in": "query",
"schema": {
@@ -11499,7 +11499,7 @@
"credentialId": {
"type": "number"
},
"domainWideDelegationCredentialId": {
"delegationCredentialId": {
"type": "string"
}
},
@@ -15170,7 +15170,7 @@
"client_id"
]
},
"CreateDwdInput": {
"CreateDelegationCredentialInput": {
"type": "object",
"properties": {
"workspacePlatformSlug": {
@@ -15214,7 +15214,7 @@
"slug"
]
},
"DwdOutput": {
"DelegationCredentialOutput": {
"type": "object",
"properties": {
"id": {
@@ -15251,7 +15251,7 @@
"updatedAt"
]
},
"CreateDwdOutput": {
"CreateDelegationCredentialOutput": {
"type": "object",
"properties": {
"status": {
@@ -15263,7 +15263,7 @@
]
},
"data": {
"$ref": "#/components/schemas/DwdOutput"
"$ref": "#/components/schemas/DelegationCredentialOutput"
}
},
"required": [
@@ -15271,7 +15271,7 @@
"data"
]
},
"UpdateDwdInput": {
"UpdateDelegationCredentialInput": {
"type": "object",
"properties": {
"enabled": {
@@ -15292,7 +15292,7 @@
}
}
},
"UpdateDwdOutput": {
"UpdateDelegationCredentialOutput": {
"type": "object",
"properties": {
"status": {
@@ -15304,7 +15304,7 @@
]
},
"data": {
"$ref": "#/components/schemas/DwdOutput"
"$ref": "#/components/schemas/DelegationCredentialOutput"
}
},
"required": [
@@ -15914,7 +15914,7 @@
"credentialId": {
"type": "number"
},
"domainWideDelegationCredentialId": {
"delegationCredentialId": {
"type": "string",
"nullable": true
}
@@ -15955,7 +15955,7 @@
"credentialId": {
"type": "number"
},
"domainWideDelegationCredentialId": {
"delegationCredentialId": {
"type": "string",
"nullable": true
}
@@ -15976,7 +15976,7 @@
"credentialId": {
"type": "number"
},
"domainWideDelegationCredentialId": {
"delegationCredentialId": {
"type": "string",
"nullable": true
},
@@ -16023,7 +16023,7 @@
"type": "number",
"nullable": true
},
"domainWideDelegationCredentialId": {
"delegationCredentialId": {
"type": "string",
"nullable": true
},
@@ -18383,7 +18383,7 @@
"example": "https://caldav.icloud.com/26962146906/calendars/1644422A-1945-4438-BBC0-4F0Q23A57R7S/",
"description": "Unique identifier used to represent the specfic calendar, as returned by the /calendars endpoint"
},
"domainWideDelegationCredentialId": {
"delegationCredentialId": {
"type": "string"
}
},
@@ -168,10 +168,10 @@ const organizationAdminKeys = [
"OAuth Clients",
"SSO",
"directory_sync",
"domain_wide_delegation",
"delegation_credential",
];
const useTabs = ({ isDwdEnabled }: { isDwdEnabled: boolean }) => {
const useTabs = ({ isDelegationCredentialEnabled }: { isDelegationCredentialEnabled: boolean }) => {
const session = useSession();
const { data: user } = trpc.viewer.me.useQuery({ includePasswordAdded: true });
const orgBranding = useOrgBranding();
@@ -202,11 +202,11 @@ const useTabs = ({ isDwdEnabled }: { isDwdEnabled: boolean }) => {
});
}
// Add domain-wide-delegation menu item only if feature flag is enabled
if (isDwdEnabled) {
// Add delegation-credential menu item only if feature flag is enabled
if (isDelegationCredentialEnabled) {
newArray.push({
name: "domain_wide_delegation",
href: "/settings/organizations/domain-wide-delegation",
name: "delegation_credential",
href: "/settings/organizations/delegation-credential",
});
}
@@ -243,7 +243,7 @@ const useTabs = ({ isDwdEnabled }: { isDwdEnabled: boolean }) => {
if (isAdmin) return true;
return !adminRequiredKeys.includes(tab.name);
});
}, [isAdmin, orgBranding, isOrgAdminOrOwner, user, isDwdEnabled]);
}, [isAdmin, orgBranding, isOrgAdminOrOwner, user, isDelegationCredentialEnabled]);
return processTabsMemod;
};
@@ -440,7 +440,9 @@ const SettingsSidebarContainer = ({
enabled: !!session.data?.user?.org && !currentOrgProp,
});
const tabsWithPermissions = useTabs({ isDwdEnabled: !!_currentOrg?.features?.domainWideDelegation });
const tabsWithPermissions = useTabs({
isDelegationCredentialEnabled: !!_currentOrg?.features?.delegationCredential,
});
const { data: _otherTeams } = trpc.viewer.organizations.listOtherTeams.useQuery(undefined, {
enabled: !!session.data?.user?.org && !otherTeamsProp,
@@ -1,12 +1,12 @@
import { _generateMetadata, getTranslate } from "app/_utils";
import DomainWideDelegationList from "@calcom/features/ee/organizations/pages/settings/domainWideDelegation";
import DelegationCredentialList from "@calcom/features/ee/organizations/pages/settings/delegationCredential";
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
export const generateMetadata = async () =>
await _generateMetadata(
(t) => t("domain_wide_delegation"),
(t) => t("domain_wide_delegation_description")
(t) => t("delegation_credential"),
(t) => t("delegation_credential_description")
);
const Page = async () => {
@@ -15,9 +15,9 @@ const Page = async () => {
return (
<SettingsHeader
borderInShellHeader
title={t("domain_wide_delegation")}
description={t("domain_wide_delegation_description")}>
<DomainWideDelegationList />
title={t("delegation_credential")}
description={t("delegation_credential_description")}>
<DelegationCredentialList />
</SettingsHeader>
);
};
@@ -1,112 +0,0 @@
## Version 1.0
### Release Plan
1. Read the document(domain-wide-delegation.md) and acknowledge it.
3. Deploy:
1. Follow "Setting up Domain-Wide Delegation for Google Calendar API" in domain-wide-delegation.md to create Service Account and create a workspace.
2. Merge PR(without Calendar Cache Support) and then deploy.
4. Enable for i.cal.com:
1. Disable Calendar Cache for i.cal.com
2. Enable DWD for i.cal.com first and then test there
3. Wait for 1-2 days and keep monitoring the errors in Sentry and Axiom.
5. Merge & Deploy the Calendar Cache support(with DWD) PR
1. Enable Calendar Cache back for i.cal.com
2. Observe the errors in Sentry and Axiom.
6. Enable for a big customer:
1. Wait for a week and keep monitoring the errors in Sentry and Axiom.
7. Use delegatedCredentialsFirst instead of delegatedCredentialsLast in EventManager.ts
- Observe errors in Sentry and Axiom.
## Manual Testing
- V2/V1 APIs - There could be problem if DWD credentials aren't supported there. Because as organization enables DWD and new users won't need to connect their calendars in the old way. e.g. getBusyCalendarTimes seem to be used there in V2.
- [x] Request reshedule - cancellation of meeting in calendar verification
- [x] Isolation of DWD credentials for different organizations
- [x] Using same domain name for different organization isn't allowed. It is restricted during creation of DWD.
- [x] Location Change of a booking to Google Meet(from Cal Video)
- [x] Google Meet/Google Calendar
- [x] Shows up installed on apps/google-meet and apps/instaled/conferencing
- [x] Can't remove the app
- [x] Seated Event
- [x] Seat cancellation removes calendar invite
- [x] Onboarding
- [x] When DWD is not enabled, the flow works.
- [x] When DWD is enabled, Google Calendar is pre-installed and Destination Calendar and Selected Calendar are configurable. On next step, Google Meet is pre-installed and shown at the top and could be set as default.
- [x] User Level Selected Calendar and Destination Calendars are honored - verified through booking the user.
- [ ] Event Type Selected Calendar and Destination Calendar
- [ ] Dynamic Group Booking
- [x] Owner must have verified email to enable DWD
- [x] RR Team Event
- [x] Booking
- Unavailable slot isn't available for booking. Unavailable user isn't used.
- [x] Reroute
- [x] Reassign
- [x] Troubleshooter
- [x] Shows busy times from Calendar
- [ ] Calendar Cache[TO BE TESTED LATER]
- [ ] Event Type Selected Calendar caching test
- [ ] User Selected Calendar caching test
### Important
- **Performance**
- There could be 100s of users in an organization with already connected calendars. Enabling DWD adds a duplicate credential(in-memory) for each of them.
- Because a credential isn't aware of which externalIds it has access to(without connecting with Google Calendar API itself), we can't identify which credential is for which SelectedCalendar and thus we can't de-duplicate them. This has an impact on fetching the availability and thus for a team event with x participants, we could have 2x requests to Google Calendar API. Because for big value of x, user might be using routing already with team members and thus the actual value of x might be much lower. So, we could be fine. Also, we would have Calendar Cache enabled there already to reduce the requests.
- Bugs
- [x] For RR event, if I am able to see the slots for a blocked day in calendar(by temporarily unselecting the calendar), and then we enable the calendar back, the blocked day can be booked.
- [x] Not using Goole Meet with Default conferencing app.
- [x] Duplicate Calendar Events in Google Calendar when choosing non-primary calendar as destination. No idea why this is happening.
- The issue was in getAttendees in Google CalendarService not using externalCalendarId.
- [x] Restrict toggling based on email verified
- [x] Duplicate Calendar connections in 'apps/installed' if a user already had connected calendar and DWD is enabled.
- [x] Calendar Cache has credentialId column which isn't applicable for DWD(Solution: Added userId there)
- [x] Troubleshooter
- [x] Shows busy times from Claendar
- [x] If a user has connected a calendar, and then DWD is enabled.
- Tested various scenarios for it
- [x] Inviting a new user.
- Verified that Google Calendar is shown pre-installed.
- How about Google Meet(which depends on Google Calendar) - Correctly shows up as installed.
- TODO:
- [ ] Add experimental/Beta flag in DWD
- [ ] Consideration of multiple domains email in a single team event. We might need to consider all the member's domains in the team event when fetching availabilituy.
- [x] Performance
- [x] Available Slots and booking flow shouldn't slow down. Right now the querying logic is not optimized. We query per team member, we should do one query - This is fixed and took quite a refactor but it is cleaner code now.
- [ ] Tag all DWD related logs and errors with "DWD:"
- [x] Troubleshooter
- [x] Google CalendarService unit tests to verify that if DWD credential is provided it uses impersonation to access API otherwise it uses regular user credential API.
- [x] setDestinationCalendar.handler.ts tests to verify that when DWD is enabled it still correctly sets the destination calendar.
- [x] getConnectedDestinationCalendars tests.
- [x] Creating DWD shouldn't immediately enable it. Enabling has separate check to confirm if it is actually configured in google workspace
- [x] Added check to avoid adding same domain for a workspace platform in another organization if it is already enabled in some other organization
- [x] Don't show dwd in menu for non-org-admin users - It errors with something_went_wrong right now
- [x] Don't allow disabled platform to be selected in the UI for creation.
- We have disabled coming the disabled platform to be coming into the list that effectively disables edit of existing dwd and creation of new dwd for that platform.
- [x] Where should we show the user the client ID to enable domain wide delegation?
- [x] It must be shown to the organization owner/admin only
- [x] There could be multiple checkboxes per domain to enable domain wide delegation for a domain
- [x] Which domain to allow
- Any domain can be added by a user
- [x] Support multiple domains in DomainWideDelegation schema for an organization
- [x] Use the domain as well to identify if the domain wide delegation is enabled
- [x] Before enabling Domain-wide delegation, there should be a check to ensure that the clientID has been added to the Workspace Platform
- [x] We should allow setting default conferencing app during onboarding
### Follow-up release
- [ ] Confirmation for DwD toggling off
- [ ] Confirmation for DwD deletion - Deletion isn't there at the moment.
- [ ] Make Google Meet "show up" as default conferencing app when DWD for Google is enabled.
- [ ] Profile pic from Google with DWD might not be working - Fix it.
### Security
- [x] We don't let any one user see the added service account key from UI.
- [ ] We intend to implement Workload Identity Federation in the future.
### Documentation
- After enabling domain-wide delegation, the credential is shown pre-installed and the connection can't be removed(or the app can't be uninstalled by user)
- Steps
- App admin will first create a Workspace Platform and then organization owner/admin can enable domain-wide delegation for a domain
- As soon as domain-wide delegation is created, it would start taking preference over the personal credentials of the organization members and it would be used for that.
Version-2.0
- Workload Identity Federation to ensure that the service account key is never stored in DB.
@@ -1,104 +0,0 @@
## Setting up Domain-Wide Delegation for Google Calendar API
Step 1: Create a Google Cloud Project
Before you can create a service account, you'll need to set up a Google Cloud project.
1. Create a Google Cloud Project:
- Go to the Google Cloud Console
- Select Create Project
- Give your project a name and select your billing account (if applicable)
- Click Create
2. Enable the Google Calendar API:
1. Go to the Google Cloud Console
2. Select API & Services → Library
3. Search for "Google Calendar API"
4. Click Enable
Step 2: Create a Service Account
A service account is needed to act on behalf of users
1. Navigate to the Service Accounts page:
- In the Google Cloud Console, go to IAM & Admin → Service Accounts
2. Create a New Service Account:
- Click on Create Service Account
- Give your service account a name and description
- Click Create and Continue
Step 3: To Be taken by Cal.com instance admin:
- Create a Workspace Platform with slug="google". Slug has to be exactly this. This is how we know we need to use Google Calendar and Google Meet.
Last Step (To Be Taken By Cal.com organization Owner/Admin): Assign Specific API Permissions via OAuth Scopes:
- Create DWD with workspace platform "google"
- User must be a member of the Google Workspace to be able to enable DWD as there is a validation if the user's calendar can be accessed through the service account
- Get the Client ID from there
- Go to your Google Admin Console (admin-google-com)
- Navigate to Security → Access and Data Controls -> API controls -> Manage Domain-Wide Delegation
- Here, you'll authorize the Client ID(Unique ID) to access the Google Calendar API
- Add the necessary API scopes for Google Calendar(Full access to Google Calendar)
https://www.googleapis.com/auth/calendar
## Onboarding Improvement
- Just adding a member to the organization would do the following:
- Member to receive events in their calendar, even if they don't login to their account and complete the onboarding process.
- The booking location would be Google Meet, even if the user hasn't set it as default(Though Cal Video would show up as default, but we still use Google Meet in this case. We will fix it later.)
- It would still not use their calendar for conflict checking, but user can complete the onboarding(just select one calendar there for conflict checking)
- Onboarding process: Google Calendar is pre-installed for any new member of the organization(assuming the user has an email of the DWD domain) and Destination Calendar and Selected Calendar are configurable. On next step, Google Meet is pre-installed and shown at the top and could be set as default.
## Restrictions after enabling DWD
- Enabling DWD for a particular workspace in Cal.com(only google supported at the moment) disables the user from disconnecting that credential.
## Who can create DWD and enable it?
- Only the owner/admin of the organization can create DWD
- Only the owner/admin of the organization can enable the created DWD. Following additional requirements are there:
- The client ID must be added to the Google Admin Console
- The user's email must be a member of the Google Workspace
- The user's email must be verified.
## Disabling DWD
- Disabling DWD maintains the user's preferences in terms of SelectedCalendars and DestinationCalendar.
## Developer Notes
### Terminology
- DWD Credential: A DWD service account key along with user's email becomes the DWD Credential which is an alternative to regular Credential in DB.
- DWD: Domain Wide Delegation
- non-dwd credential: Regular credentials that are stored in Credentials table
### How DWD works
- We use the Cal.com user's email to impersonate that user using DWD Credential(which is just a service account key at the moment)
- That gives us read/write permission to get availability of the user and create new events in their calendar.
### What is a DWD Credential?
- A DWD service account key along with user's email becomes the DWD Credential which is an alternative to regular Credential in DB.
- DWD doesn't completely replace the regular credentials. DWD Credential gives access to the cal.com user's email in Google Calendar. So, if the user needs to connect to some other email's calendar, we need to use the regular credentials.
### Important Points
- No Credential table entry is created when enabling DWD. The workspace platform's related apps will be considered as "installed" for the users with email matching dwd domain. An in-memory credential like object is created for this purpose. It allows avoiding creation of thousands of records for all the members of the organization when dwd is enabled.
- DWD Credential is applicable to Users only.
- For team, we don't use dwd credential as you can impersonate a user and not team through Dwd credential. Currently supported apps(Google Calendar and Google Meet) don't support team installation, so we could simply allow enabling DWD without any issues.
- Disabling a workspace platform stops it from being used for any new organizations and also disables any DWD using the workspace platform from being edited.
- It still all existing DWDs to keep on working
- Adding any number of DWDs for a particular workspace always gives the same Client ID as DWD uses the workspace's default Service Account.
- We should disable DWD and not delete it when we want to stop using it temporarily. Deleting DWD also removes all the seletedCalendar entries connected to it.
### How apps/installed loads the credentials
1. Identify the logged in user's email
2. Identify the domainWideDelegations for that email's domain
3. Build in-memory credentials for the domainWideDelegations and use them along with the actual credentials(that user might have connected) of the user
4. We don't show the non-dwd connected calendar(if there is a corresponding dwd connected calendar). Though we use the non-dwd credentials to identify the selected calendars, for the dwd connected calendar.
### Impact on existing users booking flow
- There should be no impact on availability on enabling DWD because we keep on using the existing credentials along with new DWD credential.
- When booking the event, we sort the credentials with DWD credentials last, so there should be no impact on creating calendar events.
- NOTE: We will followup with sorting the credentials with DWD credentials first in a followup PR(They are preferred because they don't expire)
### Impact on APIs - [ To Verify ]
- We don't support DWD through APIs yet. So, all existing APIs would continue to work with non-dwd credentials only.
### Notes when testing locally
- You need to enable the feature through feature flag.
- You could use Acme org and login as owner1-acme@example.com
- Make sure to change the email of the user above to your workspace owner's email(other member's email might also work). This is necessary otherwise you won't be able to enable DWD for the organization.
- Note: After changing the email, you would have to logout and login again as required by NextAuth
@@ -17,7 +17,7 @@ const selectedCalendarSelectSchema = z.object({
integration: z.string(),
externalId: z.string(),
credentialId: z.coerce.number(),
domainWideDelegationCredentialId: z.string().nullish().default(null),
delegationCredentialId: z.string().nullish().default(null),
eventTypeId: z.coerce.number().nullish(),
});
@@ -65,7 +65,7 @@ async function postHandler(req: NextRequest) {
const user = await authMiddleware();
const body = await req.json();
const { integration, externalId, credentialId, eventTypeId, domainWideDelegationCredentialId } =
const { integration, externalId, credentialId, eventTypeId, delegationCredentialId } =
selectedCalendarSelectSchema.parse(body);
await SelectedCalendarRepository.upsert({
@@ -73,7 +73,7 @@ async function postHandler(req: NextRequest) {
integration,
externalId,
credentialId,
domainWideDelegationCredentialId,
delegationCredentialId,
eventTypeId: eventTypeId ?? null,
});
@@ -113,7 +113,7 @@ const AppConnectionItem = (props: IAppConnectionItem) => {
</Button>
)}
/>
{/* It is possible that app is already installed here during onboarding due to Domain Wide Delegation enabled at organization level. We allow the user to set it as default */}
{/* It is possible that app is already installed here during onboarding due to Delegation Credential enabled at organization level. We allow the user to set it as default */}
{installed && !isDefault && (
<Button
color="secondary"
@@ -14,7 +14,7 @@ interface IConnectedCalendarItem {
userId?: number | undefined;
integration?: string | undefined;
externalId: string;
domainWideDelegationCredentialId: string | null;
delegationCredentialId: string | null;
}[];
}
@@ -62,7 +62,7 @@ const ConnectedCalendarItem = (prop: IConnectedCalendarItem) => {
type={integrationType}
isChecked={calendar.isSelected}
isLastItemInList={i === calendars.length - 1}
domainWideDelegationCredentialId={calendar.domainWideDelegationCredentialId}
delegationCredentialId={calendar.delegationCredentialId}
/>
))}
</ul>
@@ -22,7 +22,7 @@ const ConnectedVideoStep = (props: ConnectedAppStepProps) => {
* Both props together sort by most popular first, then by installed first.
* So, installed apps are always shown at the top, followed by remaining apps sorted by descending popularity.
*
* This is done because there could be not so popular app already installed by the admin(e.g. through Domain-Wide Delegation)
* This is done because there could be not so popular app already installed by the admin(e.g. through Delegation Credential)
* and we want to show it at the top so that user can set it as default if he wants to.
*/
sortByMostPopular: true,
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { delegationCredentialRouter } from "@calcom/trpc/server/routers/viewer/delegationCredential/_router";
export default createNextApiHandler(delegationCredentialRouter);
@@ -1,4 +0,0 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { domainWideDelegationRouter } from "@calcom/trpc/server/routers/viewer/domainWideDelegation/_router";
export default createNextApiHandler(domainWideDelegationRouter);
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "عمَّ تدور هذه الجلسة؟",
"add_to_team": "إضافة إلى الفريق",
"remove_users_from_org": "إزالة مستخدمين من مؤسسة",
"domain_wide_delegation": "تفويض على مستوى النطاق",
"domain_wide_delegation_description": "يتيح لك التفويض على مستوى النطاق إدارة الوصول إلى تقويمات Google Workspace لمؤسستك.",
"delegation_credential": "تفويض على مستوى النطاق",
"delegation_credential_description": "يتيح لك التفويض على مستوى النطاق إدارة الوصول إلى تقويمات Google Workspace لمؤسستك.",
"add_client_id_in_google_workspace_with_below_scope": "أضف معرف العميل هذا في Google Workspace مع النطاق أدناه",
"domain_wide_delegation_enabled": "تم تمكين التفويض على مستوى النطاق",
"domain_wide_delegation_disabled": "تم تعطيل التفويض على مستوى النطاق",
"add_domain_wide_delegation": "إضافة تفويض على مستوى النطاق",
"edit_domain_wide_delegation": "تعديل التفويض على مستوى النطاق",
"delegation_credential_enabled": "تم تمكين التفويض على مستوى النطاق",
"delegation_credential_disabled": "تم تعطيل التفويض على مستوى النطاق",
"add_delegation_credential": "إضافة تفويض على مستوى النطاق",
"edit_delegation_credential": "تعديل التفويض على مستوى النطاق",
"domain": "النطاق",
"no_workspace_platforms": "لا توجد منصات عمل",
"workspace_platform": "منصة العمل",
@@ -2287,7 +2287,7 @@
"slug": "المعرف المختصر",
"service_account_key": "مفتاح JSON لحساب الخدمة",
"edit_service_account_key": "تعديل مفتاح JSON لحساب الخدمة",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "يقيد التفويض على مستوى النطاق إضافة أكثر من تثبيت واحد",
"delegation_credential_restricts_adding_more_than_one_installation": "يقيد التفويض على مستوى النطاق إضافة أكثر من تثبيت واحد",
"app_successfully_installed_and_is_using_delegated_credentials": "تم تثبيت التطبيق بنجاح ويستخدم بيانات اعتماد مفوضة",
"remove_users_from_org_confirm": "هل أنت متأكد من أنك تريد إزالة هذا العدد من المستخدمين {{userCount}} من هذه المؤسسة؟",
"remove_users_from_team": "إزالة المستخدمين من الفريق",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "حدث خطأ غير متوقع. يرجى المحاولة مرة أخرى لاحقًا",
"select_oAuth_client": "اختر عميل OAuth",
"on_every_instance": "في كل مثيل",
"domain_wide_delegation_feature_not_enabled": "ميزة التفويض على مستوى النطاق غير مفعلة لمؤسستك",
"delegation_credential_feature_not_enabled": "ميزة التفويض على مستوى النطاق غير مفعلة لمؤسستك",
"internal_booking_note": "ملاحظة داخلية",
"internal_note_cancellation_reason": "السبب المشارك مع العميل (اختياري)",
"internal_booking_note_description": "هذه الملاحظة ستكون مرئية فقط لأعضاء فريقك",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Bu görüş nə haqqındadır?",
"add_to_team": "Komandaya əlavə et",
"remove_users_from_org": "İstifadəçiləri təşkilatdan çıxarın",
"domain_wide_delegation": "Domen səviyyəsində səlahiyyət",
"domain_wide_delegation_description": "Domen səviyyəsində səlahiyyət təşkilatınız üçün Google Workspace təqvimlərinə girişi idarə etməyə imkan verir.",
"delegation_credential": "Domen səviyyəsində səlahiyyət",
"delegation_credential_description": "Domen səviyyəsində səlahiyyət təşkilatınız üçün Google Workspace təqvimlərinə girişi idarə etməyə imkan verir.",
"add_client_id_in_google_workspace_with_below_scope": "Bu Client Id-ni aşağıdakı əhatə dairəsi ilə Google Workspace-ə əlavə edin",
"domain_wide_delegation_enabled": "Domen səviyyəsində səlahiyyət aktivdir",
"domain_wide_delegation_disabled": "Domen səviyyəsində səlahiyyət deaktivdir",
"add_domain_wide_delegation": "Domen səviyyəsində səlahiyyət əlavə et",
"edit_domain_wide_delegation": "Domen səviyyəsində səlahiyyəti redaktə et",
"delegation_credential_enabled": "Domen səviyyəsində səlahiyyət aktivdir",
"delegation_credential_disabled": "Domen səviyyəsində səlahiyyət deaktivdir",
"add_delegation_credential": "Domen səviyyəsində səlahiyyət əlavə et",
"edit_delegation_credential": "Domen səviyyəsində səlahiyyəti redaktə et",
"domain": "Domen",
"no_workspace_platforms": "İş sahəsi platformaları yoxdur",
"workspace_platform": "İş sahəsi platforması",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "Servis hesabı açarı JSON",
"edit_service_account_key": "Servis hesabı açarı JSON-u redaktə et",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Domen səviyyəsində səlahiyyət birdən çox quraşdırma əlavə etməyi məhdudlaşdırır",
"delegation_credential_restricts_adding_more_than_one_installation": "Domen səviyyəsində səlahiyyət birdən çox quraşdırma əlavə etməyi məhdudlaşdırır",
"app_successfully_installed_and_is_using_delegated_credentials": "Tətbiq uğurla quraşdırıldı və səlahiyyətli etimadnamələrdən istifadə edir",
"remove_users_from_org_confirm": "{{userCount}} istifadəçini bu təşkilatdan çıxarmaq istədiyinizə əminsinizmi?",
"remove_users_from_team": "İstifadəçiləri komandadan çıxarın",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Gözlənilməz bir xəta baş verdi. Zəhmət olmasa bir az sonra yenidən cəhd edin",
"select_oAuth_client": "OAuth müştərisini seçin",
"on_every_instance": "Hər nümunədə",
"domain_wide_delegation_feature_not_enabled": "Domen səviyyəsində səlahiyyət funksiyası təşkilatınız üçün aktivləşdirilməyib",
"delegation_credential_feature_not_enabled": "Domen səviyyəsində səlahiyyət funksiyası təşkilatınız üçün aktivləşdirilməyib",
"internal_booking_note": "Daxili Qeyd",
"internal_note_cancellation_reason": "Müştəri ilə paylaşılan səbəb (istəyə bağlı)",
"internal_booking_note_description": "Bu qeyd yalnız komanda üzvləriniz tərəfindən görünəcək",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "За какво е тази среща?",
"add_to_team": "Добави към екип",
"remove_users_from_org": "Премахни потребители от организацията",
"domain_wide_delegation": "Делегиране на ниво домейн",
"domain_wide_delegation_description": "Делегирането на ниво домейн ви позволява да управлявате достъпа до Google Workspace календари за вашата организация.",
"delegation_credential": "Делегиране на ниво домейн",
"delegation_credential_description": "Делегирането на ниво домейн ви позволява да управлявате достъпа до Google Workspace календари за вашата организация.",
"add_client_id_in_google_workspace_with_below_scope": "Добавете този Client Id в Google Workspace със следния обхват",
"domain_wide_delegation_enabled": "Делегирането на ниво домейн е активирано",
"domain_wide_delegation_disabled": "Делегирането на ниво домейн е деактивирано",
"add_domain_wide_delegation": "Добавяне на делегиране на ниво домейн",
"edit_domain_wide_delegation": "Редактиране на делегиране на ниво домейн",
"delegation_credential_enabled": "Делегирането на ниво домейн е активирано",
"delegation_credential_disabled": "Делегирането на ниво домейн е деактивирано",
"add_delegation_credential": "Добавяне на делегиране на ниво домейн",
"edit_delegation_credential": "Редактиране на делегиране на ниво домейн",
"domain": "Домейн",
"no_workspace_platforms": "Няма работни платформи",
"workspace_platform": "Работна платформа",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "JSON ключ на служебния акаунт",
"edit_service_account_key": "Редактиране на JSON ключа на служебния акаунт",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Делегирането на ниво домейн ограничава добавянето на повече от една инсталация",
"delegation_credential_restricts_adding_more_than_one_installation": "Делегирането на ниво домейн ограничава добавянето на повече от една инсталация",
"app_successfully_installed_and_is_using_delegated_credentials": "Приложението е успешно инсталирано и използва делегирани идентификационни данни",
"remove_users_from_org_confirm": "Сигурни ли сте, че искате да премахнете {{userCount}} потребители от тази организация?",
"remove_users_from_team": "Премахни потребители от екипа",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Възникна неочаквана грешка. Моля, опитайте отново по-късно",
"select_oAuth_client": "Изберете OAuth клиент",
"on_every_instance": "При всяко изпълнение",
"domain_wide_delegation_feature_not_enabled": "Функцията за делегиране на ниво домейн не е активирана за вашата организация",
"delegation_credential_feature_not_enabled": "Функцията за делегиране на ниво домейн не е активирана за вашата организация",
"internal_booking_note": "Вътрешна бележка",
"internal_note_cancellation_reason": "Причина, споделена с клиента (по избор)",
"internal_booking_note_description": "Тази бележка ще бъде видима само за членовете на вашия екип",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "De què tracta aquesta reunió?",
"add_to_team": "Afegir a l'equip",
"remove_users_from_org": "Eliminar usuaris de l'organització",
"domain_wide_delegation": "Delegació per a tot el domini",
"domain_wide_delegation_description": "La delegació per a tot el domini us permet gestionar l'accés als calendaris de Google Workspace per a la vostra organització.",
"delegation_credential": "Delegació per a tot el domini",
"delegation_credential_description": "La delegació per a tot el domini us permet gestionar l'accés als calendaris de Google Workspace per a la vostra organització.",
"add_client_id_in_google_workspace_with_below_scope": "Afegiu aquest ID de client a Google Workspace amb l'abast següent",
"domain_wide_delegation_enabled": "Delegació per a tot el domini activada",
"domain_wide_delegation_disabled": "Delegació per a tot el domini desactivada",
"add_domain_wide_delegation": "Afegir delegació per a tot el domini",
"edit_domain_wide_delegation": "Editar delegació per a tot el domini",
"delegation_credential_enabled": "Delegació per a tot el domini activada",
"delegation_credential_disabled": "Delegació per a tot el domini desactivada",
"add_delegation_credential": "Afegir delegació per a tot el domini",
"edit_delegation_credential": "Editar delegació per a tot el domini",
"domain": "Domini",
"no_workspace_platforms": "No hi ha plataformes de workspace",
"workspace_platform": "Plataforma workspace",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "JSON de la clau del compte de servei",
"edit_service_account_key": "Editar JSON de la clau del compte de servei",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "La delegació per a tot el domini restringeix afegir més d'una instal·lació",
"delegation_credential_restricts_adding_more_than_one_installation": "La delegació per a tot el domini restringeix afegir més d'una instal·lació",
"app_successfully_installed_and_is_using_delegated_credentials": "L'aplicació s'ha instal·lat correctament i està utilitzant credencials delegades",
"remove_users_from_org_confirm": "Estàs segur que vols eliminar {{userCount}} usuaris d'aquesta organització?",
"remove_users_from_team": "Eliminar usuaris de l'equip",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "S'ha produït un error inesperat. Si us plau, torna-ho a provar més tard",
"select_oAuth_client": "Selecciona el client OAuth",
"on_every_instance": "A cada instància",
"domain_wide_delegation_feature_not_enabled": "La funció de delegació per a tot el domini no està activada per a la vostra organització",
"delegation_credential_feature_not_enabled": "La funció de delegació per a tot el domini no està activada per a la vostra organització",
"internal_booking_note": "Nota interna",
"internal_note_cancellation_reason": "Motiu compartit amb el client (opcional)",
"internal_booking_note_description": "Aquesta nota només serà visible per als membres del teu equip",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Čeho se tato schůzka týká?",
"add_to_team": "Přidat do týmu",
"remove_users_from_org": "Odstranění uživatelů z organizace",
"domain_wide_delegation": "Delegování na úrovni domény",
"domain_wide_delegation_description": "Delegování na úrovni domény vám umožňuje spravovat přístup ke kalendářům Google Workspace pro vaši organizaci.",
"delegation_credential": "Delegování na úrovni domény",
"delegation_credential_description": "Delegování na úrovni domény vám umožňuje spravovat přístup ke kalendářům Google Workspace pro vaši organizaci.",
"add_client_id_in_google_workspace_with_below_scope": "Přidejte toto Client ID do Google Workspace s následujícím rozsahem oprávnění",
"domain_wide_delegation_enabled": "Delegování na úrovni domény je povoleno",
"domain_wide_delegation_disabled": "Delegování na úrovni domény je zakázáno",
"add_domain_wide_delegation": "Přidat delegování na úrovni domény",
"edit_domain_wide_delegation": "Upravit delegování na úrovni domény",
"delegation_credential_enabled": "Delegování na úrovni domény je povoleno",
"delegation_credential_disabled": "Delegování na úrovni domény je zakázáno",
"add_delegation_credential": "Přidat delegování na úrovni domény",
"edit_delegation_credential": "Upravit delegování na úrovni domény",
"domain": "Doména",
"no_workspace_platforms": "Žádné workspace platformy",
"workspace_platform": "Workspace platforma",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "JSON klíč servisního účtu",
"edit_service_account_key": "Upravit JSON klíč servisního účtu",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Delegování na úrovni domény omezuje přidání více než jedné instalace",
"delegation_credential_restricts_adding_more_than_one_installation": "Delegování na úrovni domény omezuje přidání více než jedné instalace",
"app_successfully_installed_and_is_using_delegated_credentials": "Aplikace byla úspěšně nainstalována a používá delegované přihlašovací údaje",
"remove_users_from_org_confirm": "Opravdu chcete odebrat uživatele ({{userCount}}) z této organizace?",
"remove_users_from_team": "Odstranit uživatele z týmu",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Došlo k neočekávané chybě. Zkuste to prosím později",
"select_oAuth_client": "Vybrat OAuth klienta",
"on_every_instance": "Na každé instanci",
"domain_wide_delegation_feature_not_enabled": "Funkce delegování na úrovni domény není pro vaši organizaci povolena",
"delegation_credential_feature_not_enabled": "Funkce delegování na úrovni domény není pro vaši organizaci povolena",
"internal_booking_note": "Interní poznámka",
"internal_note_cancellation_reason": "Důvod sdílený se zákazníkem (volitelné)",
"internal_booking_note_description": "Tato poznámka bude viditelná pouze pro členy vašeho týmu",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Hvad handler dette møde om?",
"add_to_team": "Tilføj til team",
"remove_users_from_org": "Fjern brugere fra organisation",
"domain_wide_delegation": "Domæneomfattende delegation",
"domain_wide_delegation_description": "Domæneomfattende delegation giver dig mulighed for at administrere adgang til Google Workspace-kalendere for din organisation.",
"delegation_credential": "Domæneomfattende delegation",
"delegation_credential_description": "Domæneomfattende delegation giver dig mulighed for at administrere adgang til Google Workspace-kalendere for din organisation.",
"add_client_id_in_google_workspace_with_below_scope": "Tilføj dette klient-id i Google Workspace med nedenstående scope",
"domain_wide_delegation_enabled": "Domæneomfattende delegation aktiveret",
"domain_wide_delegation_disabled": "Domæneomfattende delegation deaktiveret",
"add_domain_wide_delegation": "Tilføj domæneomfattende delegation",
"edit_domain_wide_delegation": "Rediger domæneomfattende delegation",
"delegation_credential_enabled": "Domæneomfattende delegation aktiveret",
"delegation_credential_disabled": "Domæneomfattende delegation deaktiveret",
"add_delegation_credential": "Tilføj domæneomfattende delegation",
"edit_delegation_credential": "Rediger domæneomfattende delegation",
"domain": "Domæne",
"no_workspace_platforms": "Ingen workspace-platforme",
"workspace_platform": "Workspace-platform",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "Servicekontonøgle JSON",
"edit_service_account_key": "Rediger servicekontonøgle JSON",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Domæneomfattende delegation begrænser tilføjelse af mere end én installation",
"delegation_credential_restricts_adding_more_than_one_installation": "Domæneomfattende delegation begrænser tilføjelse af mere end én installation",
"app_successfully_installed_and_is_using_delegated_credentials": "Appen er installeret og bruger delegerede legitimationsoplysninger",
"remove_users_from_org_confirm": "Er du sikker på, at du vil fjerne {{userCount}} brugere fra denne organisation?",
"remove_users_from_team": "Fjern brugere fra teamet",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Der opstod en uventet fejl. Prøv venligst igen senere",
"select_oAuth_client": "Vælg OAuth-klient",
"on_every_instance": "På hver forekomst",
"domain_wide_delegation_feature_not_enabled": "Funktionen for domæneomfattende delegation er ikke aktiveret for din organisation",
"delegation_credential_feature_not_enabled": "Funktionen for domæneomfattende delegation er ikke aktiveret for din organisation",
"internal_booking_note": "Intern note",
"internal_note_cancellation_reason": "Årsag delt med kunden (valgfrit)",
"internal_booking_note_description": "Denne note vil kun være synlig for medlemmer af dit team",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Worum geht es in diesem Termin?",
"add_to_team": "Zum Team hinzufügen",
"remove_users_from_org": "Benutzer aus der Organisation entfernen",
"domain_wide_delegation": "Organisationsweite Berechtigung",
"domain_wide_delegation_description": "Die organisationsweite Berechtigung ermöglicht es Ihnen, den Zugriff auf Google Workspace-Kalender für Ihre Organisation zu verwalten.",
"delegation_credential": "Organisationsweite Berechtigung",
"delegation_credential_description": "Die organisationsweite Berechtigung ermöglicht es Ihnen, den Zugriff auf Google Workspace-Kalender für Ihre Organisation zu verwalten.",
"add_client_id_in_google_workspace_with_below_scope": "Fügen Sie diese Client-ID in Google Workspace mit dem folgenden Bereich hinzu",
"domain_wide_delegation_enabled": "Organisationsweite Berechtigung aktiviert",
"domain_wide_delegation_disabled": "Organisationsweite Berechtigung deaktiviert",
"add_domain_wide_delegation": "Organisationsweite Berechtigung hinzufügen",
"edit_domain_wide_delegation": "Organisationsweite Berechtigung bearbeiten",
"delegation_credential_enabled": "Organisationsweite Berechtigung aktiviert",
"delegation_credential_disabled": "Organisationsweite Berechtigung deaktiviert",
"add_delegation_credential": "Organisationsweite Berechtigung hinzufügen",
"edit_delegation_credential": "Organisationsweite Berechtigung bearbeiten",
"domain": "Domain",
"no_workspace_platforms": "Keine Workspace-Plattformen",
"workspace_platform": "Workspace-Plattform",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "Servicekonto-Schlüssel JSON",
"edit_service_account_key": "Servicekonto-Schlüssel JSON bearbeiten",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Die organisationsweite Berechtigung beschränkt das Hinzufügen auf eine Installation",
"delegation_credential_restricts_adding_more_than_one_installation": "Die organisationsweite Berechtigung beschränkt das Hinzufügen auf eine Installation",
"app_successfully_installed_and_is_using_delegated_credentials": "App erfolgreich installiert und verwendet delegierte Anmeldeinformationen",
"remove_users_from_org_confirm": "Sind Sie sicher, dass Sie {{userCount}} Benutzer aus dieser Organisation entfernen möchten?",
"remove_users_from_team": "Benutzer aus Team entfernen",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut",
"select_oAuth_client": "OAuth-Client auswählen",
"on_every_instance": "Bei jeder Instanz",
"domain_wide_delegation_feature_not_enabled": "Die Funktion für organisationsweite Berechtigung ist für Ihre Organisation nicht aktiviert",
"delegation_credential_feature_not_enabled": "Die Funktion für organisationsweite Berechtigung ist für Ihre Organisation nicht aktiviert",
"internal_booking_note": "Interner Vermerk",
"internal_note_cancellation_reason": "Grund, der dem Kunden mitgeteilt wird (optional)",
"internal_booking_note_description": "Dieser Vermerk ist nur für Mitglieder deines Teams sichtbar",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Ποιο είναι το θέμα της συνάντησης;",
"add_to_team": "Προσθήκη στην ομάδα",
"remove_users_from_org": "Αφαίρεση χρηστών από τον οργανισμό",
"domain_wide_delegation": "Εξουσιοδότηση σε επίπεδο τομέα",
"domain_wide_delegation_description": "Η εξουσιοδότηση σε επίπεδο τομέα σας επιτρέπει να διαχειρίζεστε την πρόσβαση στα ημερολόγια του Google Workspace για τον οργανισμό σας.",
"delegation_credential": "Εξουσιοδότηση σε επίπεδο τομέα",
"delegation_credential_description": "Η εξουσιοδότηση σε επίπεδο τομέα σας επιτρέπει να διαχειρίζεστε την πρόσβαση στα ημερολόγια του Google Workspace για τον οργανισμό σας.",
"add_client_id_in_google_workspace_with_below_scope": "Προσθέστε αυτό το Client Id στο Google Workspace με το παρακάτω scope",
"domain_wide_delegation_enabled": "Η εξουσιοδότηση σε επίπεδο τομέα είναι ενεργοποιημένη",
"domain_wide_delegation_disabled": "Η εξουσιοδότηση σε επίπεδο τομέα είναι απενεργοποιημένη",
"add_domain_wide_delegation": "Προσθήκη εξουσιοδότησης σε επίπεδο τομέα",
"edit_domain_wide_delegation": "Επεξεργασία εξουσιοδότησης σε επίπεδο τομέα",
"delegation_credential_enabled": "Η εξουσιοδότηση σε επίπεδο τομέα είναι ενεργοποιημένη",
"delegation_credential_disabled": "Η εξουσιοδότηση σε επίπεδο τομέα είναι απενεργοποιημένη",
"add_delegation_credential": "Προσθήκη εξουσιοδότησης σε επίπεδο τομέα",
"edit_delegation_credential": "Επεξεργασία εξουσιοδότησης σε επίπεδο τομέα",
"domain": "Τομέας",
"no_workspace_platforms": "Δεν υπάρχουν πλατφόρμες workspace",
"workspace_platform": "Πλατφόρμα workspace",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "JSON κλειδιού λογαριασμού υπηρεσίας",
"edit_service_account_key": "Επεξεργασία JSON κλειδιού λογαριασμού υπηρεσίας",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Η εξουσιοδότηση σε επίπεδο τομέα περιορίζει την προσθήκη περισσότερων από μία εγκαταστάσεων",
"delegation_credential_restricts_adding_more_than_one_installation": "Η εξουσιοδότηση σε επίπεδο τομέα περιορίζει την προσθήκη περισσότερων από μία εγκαταστάσεων",
"app_successfully_installed_and_is_using_delegated_credentials": "Η εφαρμογή εγκαταστάθηκε επιτυχώς και χρησιμοποιεί διαπιστευτήρια εξουσιοδότησης",
"remove_users_from_org_confirm": "Είστε βέβαιοι ότι θέλετε να αφαιρέσετε {{userCount}} χρήστες από αυτόν τον οργανισμό;",
"remove_users_from_team": "Αφαίρεση χρηστών από την ομάδα",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Παρουσιάστηκε ένα απρόσμενο σφάλμα. Παρακαλώ δοκιμάστε ξανά αργότερα",
"select_oAuth_client": "Επιλέξτε OAuth Client",
"on_every_instance": "Σε κάθε περίπτωση",
"domain_wide_delegation_feature_not_enabled": "Η λειτουργία εξουσιοδότησης σε επίπεδο τομέα δεν είναι ενεργοποιημένη για τον οργανισμό σας",
"delegation_credential_feature_not_enabled": "Η λειτουργία εξουσιοδότησης σε επίπεδο τομέα δεν είναι ενεργοποιημένη για τον οργανισμό σας",
"internal_booking_note": "Εσωτερική σημείωση",
"internal_note_cancellation_reason": "Λόγος που θα κοινοποιηθεί στον πελάτη (προαιρετικό)",
"internal_booking_note_description": "Αυτή η σημείωση θα είναι ορατή μόνο στα μέλη της ομάδας σας",
+17 -17
View File
@@ -2269,25 +2269,25 @@
"what_is_this_meeting_about": "What is this meeting about?",
"add_to_team": "Add to team",
"remove_users_from_org": "Remove users from organization",
"domain_wide_delegation": "Domain-wide delegation",
"domain_wide_delegation_description": "Domain-wide delegation allows you to manage access to Google Workspace calendars for your organization.",
"delegation_credential": "Delegation credential",
"delegation_credential_description": "Delegation credential allows you to manage access to Google Workspace calendars for your organization.",
"add_client_id_in_google_workspace_with_below_scope": "Add this Client Id in Google Workspace with the scope below",
"domain_wide_delegation_enabled": "Domain-wide delegation enabled",
"domain_wide_delegation_disabled": "Domain-wide delegation disabled",
"add_domain_wide_delegation": "Add domain-wide delegation",
"edit_domain_wide_delegation": "Edit domain-wide delegation",
"delegation_credential_enabled": "Delegation credential enabled",
"delegation_credential_disabled": "Delegation credential disabled",
"add_delegation_credential": "Add delegation credential",
"edit_delegation_credential": "Edit delegation credential",
"domain": "Domain",
"no_workspace_platforms": "No workspace platforms",
"workspace_platform": "Workspace platform",
"workspace_platforms": "Workspace platforms",
"workspace_platforms_description": "Manage workspace platforms that can be used for domain-wide delegation",
"workspace_platforms_description": "Manage workspace platforms that can be used for delegation credential",
"edit_workspace_platform": "Edit workspace platform",
"edit_service_account": "Edit service account",
"add_workspace_platform": "Add workspace platform",
"slug": "Slug",
"service_account_key": "Service account key JSON",
"edit_service_account_key": "Edit service account key JSON",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Domain-wide delegation restricts adding more than one installation",
"delegation_credential_restricts_adding_more_than_one_installation": "Delegation credential restricts adding more than one installation",
"app_successfully_installed_and_is_using_delegated_credentials": "App successfully installed and is using delegated credentials",
"remove_users_from_org_confirm": "Are you sure you want to remove {{userCount}} users from this organization?",
"remove_users_from_team": "Remove users from team",
@@ -2744,24 +2744,24 @@
"booking_limits_updated_successfully": "Booking limits updated successfully",
"you_are_unauthorized_to_make_this_change_to_the_booking": "You are unauthorized to make this change to the booking",
"add_client_id_in_google_workspace_with_below_scope": "Add this Client Id in Google Workspace with the scope below",
"domain_wide_delegation": "Domain-wide delegation",
"domain_wide_delegation_enabled": "Domain-wide delegation enabled",
"domain_wide_delegation_disabled": "Domain-wide delegation disabled",
"domain_wide_delegation_description": "Domain-wide delegation allows you to manage access to Google Workspace calendars for your organization.",
"add_domain_wide_delegation": "Add domain-wide delegation",
"edit_domain_wide_delegation": "Edit domain-wide delegation",
"delegation_credential": "Delegation credential",
"delegation_credential_enabled": "Delegation credential enabled",
"delegation_credential_disabled": "Delegation credential disabled",
"delegation_credential_description": "Delegation credential allows you to manage access to Google Workspace calendars for your organization.",
"add_delegation_credential": "Add delegation credential",
"edit_delegation_credential": "Edit delegation credential",
"domain": "Domain",
"no_workspace_platforms": "No workspace platforms",
"workspace_platform": "Workspace platform",
"workspace_platforms": "Workspace platforms",
"workspace_platforms_description": "Manage workspace platforms that can be used for domain-wide delegation",
"workspace_platforms_description": "Manage workspace platforms that can be used for delegation credential",
"edit_workspace_platform": "Edit workspace platform",
"edit_service_account": "Edit service account",
"add_workspace_platform": "Add workspace platform",
"slug": "Slug",
"service_account_key": "Service account key JSON",
"edit_service_account_key": "Edit service account key JSON",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Domain-wide delegation restricts adding more than one installation",
"delegation_credential_restricts_adding_more_than_one_installation": "Delegation credential restricts adding more than one installation",
"app_successfully_installed_and_is_using_delegated_credentials": "App successfully installed and is using delegated credentials",
"matching_members": "Matching members",
"x_matching_members": "{{x}} matching members",
@@ -2973,7 +2973,7 @@
"k_bar_ai_error": "An unexpected error occured. Please try again later",
"select_oAuth_client": "Select Oauth Client",
"on_every_instance": "On every instance",
"domain_wide_delegation_feature_not_enabled": "Domain-wide delegation feature is not enabled for your organization",
"delegation_credential_feature_not_enabled": "Delegation credential feature is not enabled for your organization",
"internal_booking_note": "Internal Note",
"internal_note_cancellation_reason": "Reason shared with customer (optional)",
"internal_booking_note_description": "This note will be visible to only members of your team",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "¿De qué se trata esta reunión?",
"add_to_team": "Agregar al equipo",
"remove_users_from_org": "Eliminar usuarios de la organización",
"domain_wide_delegation": "Delegación a nivel de dominio",
"domain_wide_delegation_description": "La delegación a nivel de dominio te permite gestionar el acceso a los calendarios de Google Workspace para tu organización.",
"delegation_credential": "Delegación a nivel de dominio",
"delegation_credential_description": "La delegación a nivel de dominio te permite gestionar el acceso a los calendarios de Google Workspace para tu organización.",
"add_client_id_in_google_workspace_with_below_scope": "Agrega este ID de cliente en Google Workspace con el siguiente alcance",
"domain_wide_delegation_enabled": "Delegación a nivel de dominio habilitada",
"domain_wide_delegation_disabled": "Delegación a nivel de dominio deshabilitada",
"add_domain_wide_delegation": "Agregar delegación a nivel de dominio",
"edit_domain_wide_delegation": "Editar delegación a nivel de dominio",
"delegation_credential_enabled": "Delegación a nivel de dominio habilitada",
"delegation_credential_disabled": "Delegación a nivel de dominio deshabilitada",
"add_delegation_credential": "Agregar delegación a nivel de dominio",
"edit_delegation_credential": "Editar delegación a nivel de dominio",
"domain": "Dominio",
"no_workspace_platforms": "No hay plataformas de workspace",
"workspace_platform": "Plataforma de workspace",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "JSON de la clave de cuenta de servicio",
"edit_service_account_key": "Editar JSON de la clave de cuenta de servicio",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "La delegación a nivel de dominio restringe agregar más de una instalación",
"delegation_credential_restricts_adding_more_than_one_installation": "La delegación a nivel de dominio restringe agregar más de una instalación",
"app_successfully_installed_and_is_using_delegated_credentials": "La aplicación se instaló correctamente y está usando credenciales delegadas",
"remove_users_from_org_confirm": "¿Estás seguro de que deseas eliminar a {{userCount}} usuarios de esta organización?",
"remove_users_from_team": "Eliminar usuarios del equipo",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Ocurrió un error inesperado. Por favor, inténtalo de nuevo más tarde",
"select_oAuth_client": "Seleccionar cliente OAuth",
"on_every_instance": "En cada instancia",
"domain_wide_delegation_feature_not_enabled": "La función de delegación a nivel de dominio no está habilitada para tu organización",
"delegation_credential_feature_not_enabled": "La función de delegación a nivel de dominio no está habilitada para tu organización",
"internal_booking_note": "Nota interna",
"internal_note_cancellation_reason": "Motivo compartido con el cliente (opcional)",
"internal_booking_note_description": "Esta nota será visible solo para los miembros de tu equipo",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "¿De qué trata esta reunión?",
"add_to_team": "Agregar al equipo",
"remove_users_from_org": "Eliminar usuarios de la organización",
"domain_wide_delegation": "Delegación a nivel de dominio",
"domain_wide_delegation_description": "La delegación a nivel de dominio te permite gestionar el acceso a los calendarios de Google Workspace para tu organización.",
"delegation_credential": "Delegación a nivel de dominio",
"delegation_credential_description": "La delegación a nivel de dominio te permite gestionar el acceso a los calendarios de Google Workspace para tu organización.",
"add_client_id_in_google_workspace_with_below_scope": "Añade este ID de cliente en Google Workspace con el siguiente alcance",
"domain_wide_delegation_enabled": "Delegación a nivel de dominio activada",
"domain_wide_delegation_disabled": "Delegación a nivel de dominio desactivada",
"add_domain_wide_delegation": "Añadir delegación a nivel de dominio",
"edit_domain_wide_delegation": "Editar delegación a nivel de dominio",
"delegation_credential_enabled": "Delegación a nivel de dominio activada",
"delegation_credential_disabled": "Delegación a nivel de dominio desactivada",
"add_delegation_credential": "Añadir delegación a nivel de dominio",
"edit_delegation_credential": "Editar delegación a nivel de dominio",
"domain": "Dominio",
"no_workspace_platforms": "No hay plataformas de trabajo",
"workspace_platform": "Plataforma de trabajo",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "JSON de la clave de cuenta de servicio",
"edit_service_account_key": "Editar JSON de la clave de cuenta de servicio",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "La delegación a nivel de dominio restringe añadir más de una instalación",
"delegation_credential_restricts_adding_more_than_one_installation": "La delegación a nivel de dominio restringe añadir más de una instalación",
"app_successfully_installed_and_is_using_delegated_credentials": "Aplicación instalada correctamente y está usando credenciales delegadas",
"remove_users_from_org_confirm": "¿Está seguro de que desea eliminar {{userCount}} usuarios de esta organización?",
"remove_users_from_team": "Eliminar usuarios del equipo",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Ocurrió un error inesperado. Por favor, inténtalo de nuevo más tarde",
"select_oAuth_client": "Seleccionar cliente OAuth",
"on_every_instance": "En cada instancia",
"domain_wide_delegation_feature_not_enabled": "La función de delegación a nivel de dominio no está habilitada para tu organización",
"delegation_credential_feature_not_enabled": "La función de delegación a nivel de dominio no está habilitada para tu organización",
"internal_booking_note": "Nota interna",
"internal_note_cancellation_reason": "Motivo compartido con el cliente (opcional)",
"internal_booking_note_description": "Esta nota solo será visible para los miembros de tu equipo",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Millest see kohtumine räägib?",
"add_to_team": "Lisa meeskonda",
"remove_users_from_org": "Eemalda kasutajad organisatsioonist",
"domain_wide_delegation": "Domeeniülene delegeerimine",
"domain_wide_delegation_description": "Domeeniülene delegeerimine võimaldab hallata juurdepääsu Google Workspace'i kalendritele teie organisatsioonis.",
"delegation_credential": "Domeeniülene delegeerimine",
"delegation_credential_description": "Domeeniülene delegeerimine võimaldab hallata juurdepääsu Google Workspace'i kalendritele teie organisatsioonis.",
"add_client_id_in_google_workspace_with_below_scope": "Lisa see kliendi ID Google Workspace'i järgmise ulatusega",
"domain_wide_delegation_enabled": "Domeeniülene delegeerimine on lubatud",
"domain_wide_delegation_disabled": "Domeeniülene delegeerimine on keelatud",
"add_domain_wide_delegation": "Lisa domeeniülene delegeerimine",
"edit_domain_wide_delegation": "Muuda domeeniülest delegeerimist",
"delegation_credential_enabled": "Domeeniülene delegeerimine on lubatud",
"delegation_credential_disabled": "Domeeniülene delegeerimine on keelatud",
"add_delegation_credential": "Lisa domeeniülene delegeerimine",
"edit_delegation_credential": "Muuda domeeniülest delegeerimist",
"domain": "Domeen",
"no_workspace_platforms": "Tööruumi platvormid puuduvad",
"workspace_platform": "Tööruumi platvorm",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "Teenusekonto võtme JSON",
"edit_service_account_key": "Muuda teenusekonto võtme JSON-i",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Domeeniülene delegeerimine piirab rohkem kui ühe installimise lisamist",
"delegation_credential_restricts_adding_more_than_one_installation": "Domeeniülene delegeerimine piirab rohkem kui ühe installimise lisamist",
"app_successfully_installed_and_is_using_delegated_credentials": "Rakendus on edukalt installitud ja kasutab delegeeritud volitusi",
"remove_users_from_org_confirm": "Kas soovite kindlasti eemaldada sellest organisatsioonist {{userCount}} kasutajat?",
"remove_users_from_team": "Eemalda kasutajad meeskonnast",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Ilmnes ootamatu viga. Palun proovi hiljem uuesti",
"select_oAuth_client": "Vali OAuth klient",
"on_every_instance": "Igal korral",
"domain_wide_delegation_feature_not_enabled": "Domeeniülese delegeerimise funktsioon ei ole teie organisatsiooni jaoks lubatud",
"delegation_credential_feature_not_enabled": "Domeeniülese delegeerimise funktsioon ei ole teie organisatsiooni jaoks lubatud",
"internal_booking_note": "Sisemine märkus",
"internal_note_cancellation_reason": "Kliendiga jagatav põhjus (valikuline)",
"internal_booking_note_description": "See märkus on nähtav ainult teie meeskonna liikmetele",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Zeri buruzko bilera da hau?",
"add_to_team": "Taldera gehitu",
"remove_users_from_org": "Erabiltzaileak erakundetik kendu",
"domain_wide_delegation": "Domeinuaren eskuordetzea",
"domain_wide_delegation_description": "Domeinuaren eskuordetzeak zure erakundeko Google Workspace egutegietarako sarbidea kudeatzeko aukera ematen dizu.",
"delegation_credential": "Domeinuaren eskuordetzea",
"delegation_credential_description": "Domeinuaren eskuordetzeak zure erakundeko Google Workspace egutegietarako sarbidea kudeatzeko aukera ematen dizu.",
"add_client_id_in_google_workspace_with_below_scope": "Gehitu bezero ID hau Google Workspace-n beheko esparruarekin",
"domain_wide_delegation_enabled": "Domeinuaren eskuordetzea gaituta",
"domain_wide_delegation_disabled": "Domeinuaren eskuordetzea desgaituta",
"add_domain_wide_delegation": "Gehitu domeinuaren eskuordetzea",
"edit_domain_wide_delegation": "Editatu domeinuaren eskuordetzea",
"delegation_credential_enabled": "Domeinuaren eskuordetzea gaituta",
"delegation_credential_disabled": "Domeinuaren eskuordetzea desgaituta",
"add_delegation_credential": "Gehitu domeinuaren eskuordetzea",
"edit_delegation_credential": "Editatu domeinuaren eskuordetzea",
"domain": "Domeinua",
"no_workspace_platforms": "Ez dago workspace plataformarik",
"workspace_platform": "Workspace plataforma",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "Zerbitzu kontuaren gako JSON",
"edit_service_account_key": "Editatu zerbitzu kontuaren gako JSON",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Domeinuaren eskuordetzeak instalazio bat baino gehiago gehitzea mugatzen du",
"delegation_credential_restricts_adding_more_than_one_installation": "Domeinuaren eskuordetzeak instalazio bat baino gehiago gehitzea mugatzen du",
"app_successfully_installed_and_is_using_delegated_credentials": "Aplikazioa ongi instalatu da eta eskuordetutako kredentzialak erabiltzen ari da",
"remove_users_from_org_confirm": "Ziur zaude {{userCount}} erabiltzaile erakunde honetatik kendu nahi dituzula?",
"remove_users_from_team": "Erabiltzaileak taldetik kendu",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Ustekabeko errore bat gertatu da. Mesedez, saiatu berriro geroago",
"select_oAuth_client": "Hautatu OAuth bezeroa",
"on_every_instance": "Instantzia bakoitzean",
"domain_wide_delegation_feature_not_enabled": "Domeinuaren eskuordetzea ez dago gaituta zure erakundearentzat",
"delegation_credential_feature_not_enabled": "Domeinuaren eskuordetzea ez dago gaituta zure erakundearentzat",
"internal_booking_note": "Barne-oharra",
"internal_note_cancellation_reason": "Bezeroari jakinarazitako arrazoia (aukerakoa)",
"internal_booking_note_description": "Ohar hau zure taldeko kideek soilik ikusiko dute",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Mikä on tapaamisen aihe?",
"add_to_team": "Lisää tiimiin",
"remove_users_from_org": "Poista käyttäjät organisaatiosta",
"domain_wide_delegation": "Toimialueen laajuinen valtuutus",
"domain_wide_delegation_description": "Toimialueen laajuinen valtuutus mahdollistaa Google Workspace -kalenterien käyttöoikeuksien hallinnan organisaatiossasi.",
"delegation_credential": "Toimialueen laajuinen valtuutus",
"delegation_credential_description": "Toimialueen laajuinen valtuutus mahdollistaa Google Workspace -kalenterien käyttöoikeuksien hallinnan organisaatiossasi.",
"add_client_id_in_google_workspace_with_below_scope": "Lisää tämä asiakastunnus Google Workspaceen alla olevalla käyttöoikeudella",
"domain_wide_delegation_enabled": "Toimialueen laajuinen valtuutus käytössä",
"domain_wide_delegation_disabled": "Toimialueen laajuinen valtuutus pois käytöstä",
"add_domain_wide_delegation": "Lisää toimialueen laajuinen valtuutus",
"edit_domain_wide_delegation": "Muokkaa toimialueen laajuista valtuutusta",
"delegation_credential_enabled": "Toimialueen laajuinen valtuutus käytössä",
"delegation_credential_disabled": "Toimialueen laajuinen valtuutus pois käytöstä",
"add_delegation_credential": "Lisää toimialueen laajuinen valtuutus",
"edit_delegation_credential": "Muokkaa toimialueen laajuista valtuutusta",
"domain": "Toimialue",
"no_workspace_platforms": "Ei työtila-alustoja",
"workspace_platform": "Työtila-alusta",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "Palvelutilin avain JSON",
"edit_service_account_key": "Muokkaa palvelutilin avaimen JSONia",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Toimialueen laajuinen valtuutus rajoittaa useamman kuin yhden asennuksen lisäämistä",
"delegation_credential_restricts_adding_more_than_one_installation": "Toimialueen laajuinen valtuutus rajoittaa useamman kuin yhden asennuksen lisäämistä",
"app_successfully_installed_and_is_using_delegated_credentials": "Sovellus asennettu onnistuneesti ja käyttää delegoituja tunnistetietoja",
"remove_users_from_org_confirm": "Haluatko varmasti poistaa {{userCount}} käyttäjää tästä organisaatiosta?",
"remove_users_from_team": "Poista käyttäjät tiimistä",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Tapahtui odottamaton virhe. Yritä myöhemmin uudelleen",
"select_oAuth_client": "Valitse OAuth-asiakassovellus",
"on_every_instance": "Jokaisessa instanssissa",
"domain_wide_delegation_feature_not_enabled": "Toimialueen laajuisen valtuutuksen ominaisuus ei ole käytössä organisaatiossasi",
"delegation_credential_feature_not_enabled": "Toimialueen laajuisen valtuutuksen ominaisuus ei ole käytössä organisaatiossasi",
"internal_booking_note": "Sisäinen muistiinpano",
"internal_note_cancellation_reason": "Asiakkaalle jaettava syy (valinnainen)",
"internal_booking_note_description": "Tämä muistiinpano näkyy vain tiimisi jäsenille",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "En quoi consiste ce rendez-vous ?",
"add_to_team": "Ajouter à l'équipe",
"remove_users_from_org": "Supprimer des utilisateurs de l'organisation",
"domain_wide_delegation": "Délégation à l'échelle du domaine",
"domain_wide_delegation_description": "La délégation à l'échelle du domaine vous permet de gérer l'accès aux calendriers Google Workspace pour votre organisation.",
"delegation_credential": "Délégation à l'échelle du domaine",
"delegation_credential_description": "La délégation à l'échelle du domaine vous permet de gérer l'accès aux calendriers Google Workspace pour votre organisation.",
"add_client_id_in_google_workspace_with_below_scope": "Ajoutez cet identifiant client dans Google Workspace avec la portée ci-dessous",
"domain_wide_delegation_enabled": "Délégation à l'échelle du domaine activée",
"domain_wide_delegation_disabled": "Délégation à l'échelle du domaine désactivée",
"add_domain_wide_delegation": "Ajouter une délégation à l'échelle du domaine",
"edit_domain_wide_delegation": "Modifier la délégation à l'échelle du domaine",
"delegation_credential_enabled": "Délégation à l'échelle du domaine activée",
"delegation_credential_disabled": "Délégation à l'échelle du domaine désactivée",
"add_delegation_credential": "Ajouter une délégation à l'échelle du domaine",
"edit_delegation_credential": "Modifier la délégation à l'échelle du domaine",
"domain": "Domaine",
"no_workspace_platforms": "Aucune plateforme workspace",
"workspace_platform": "Plateforme workspace",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "Clé JSON du compte de service",
"edit_service_account_key": "Modifier la clé JSON du compte de service",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "La délégation à l'échelle du domaine limite l'ajout à une seule installation",
"delegation_credential_restricts_adding_more_than_one_installation": "La délégation à l'échelle du domaine limite l'ajout à une seule installation",
"app_successfully_installed_and_is_using_delegated_credentials": "L'application a été installée avec succès et utilise les identifiants délégués",
"remove_users_from_org_confirm": "Voulez-vous vraiment supprimer {{userCount}} utilisateurs de cette organisation ?",
"remove_users_from_team": "Retirer des utilisateurs de l'équipe",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Une erreur inattendue s'est produite. Veuillez réessayer plus tard",
"select_oAuth_client": "Sélectionner le client OAuth",
"on_every_instance": "Sur chaque instance",
"domain_wide_delegation_feature_not_enabled": "La fonctionnalité de délégation à l'échelle du domaine n'est pas activée pour votre organisation",
"delegation_credential_feature_not_enabled": "La fonctionnalité de délégation à l'échelle du domaine n'est pas activée pour votre organisation",
"internal_booking_note": "Note interne",
"internal_note_cancellation_reason": "Raison partagée avec le client (facultatif)",
"internal_booking_note_description": "Cette note sera visible uniquement par les membres de votre équipe",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "במה עוסקת הפגישה הזו?",
"add_to_team": "הוספה לצוות",
"remove_users_from_org": "הסרת משתמשים מהארגון",
"domain_wide_delegation": "הרשאה ברמת הדומיין",
"domain_wide_delegation_description": "הרשאה ברמת הדומיין מאפשרת לך לנהל גישה ליומני Google Workspace עבור הארגון שלך.",
"delegation_credential": "הרשאה ברמת הדומיין",
"delegation_credential_description": "הרשאה ברמת הדומיין מאפשרת לך לנהל גישה ליומני Google Workspace עבור הארגון שלך.",
"add_client_id_in_google_workspace_with_below_scope": "הוסף את מזהה הלקוח הזה ב-Google Workspace עם ההרשאה הבאה",
"domain_wide_delegation_enabled": "הרשאה ברמת הדומיין מופעלת",
"domain_wide_delegation_disabled": "הרשאה ברמת הדומיין מושבתת",
"add_domain_wide_delegation": "הוסף הרשאה ברמת הדומיין",
"edit_domain_wide_delegation": "ערוך הרשאה ברמת הדומיין",
"delegation_credential_enabled": "הרשאה ברמת הדומיין מופעלת",
"delegation_credential_disabled": "הרשאה ברמת הדומיין מושבתת",
"add_delegation_credential": "הוסף הרשאה ברמת הדומיין",
"edit_delegation_credential": "ערוך הרשאה ברמת הדומיין",
"domain": "דומיין",
"no_workspace_platforms": "אין פלטפורמות עבודה",
"workspace_platform": "פלטפורמת עבודה",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "מפתח JSON של חשבון שירות",
"edit_service_account_key": "ערוך מפתח JSON של חשבון שירות",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "הרשאה ברמת הדומיין מגבילה הוספת יותר מהתקנה אחת",
"delegation_credential_restricts_adding_more_than_one_installation": "הרשאה ברמת הדומיין מגבילה הוספת יותר מהתקנה אחת",
"app_successfully_installed_and_is_using_delegated_credentials": "האפליקציה הותקנה בהצלחה ומשתמשת באישורים מואצלים",
"remove_users_from_org_confirm": "בטוח שברצונך להסיר {{userCount}} משתמשים מהארגון הזה?",
"remove_users_from_team": "הסר משתמשים מהצוות",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "אירעה שגיאה בלתי צפויה. אנא נסה שוב מאוחר יותר",
"select_oAuth_client": "בחר לקוח OAuth",
"on_every_instance": "בכל מופע",
"domain_wide_delegation_feature_not_enabled": "תכונת ההרשאה ברמת הדומיין אינה מופעלת עבור הארגון שלך",
"delegation_credential_feature_not_enabled": "תכונת ההרשאה ברמת הדומיין אינה מופעלת עבור הארגון שלך",
"internal_booking_note": "הערה פנימית",
"internal_note_cancellation_reason": "סיבה שתשותף עם הלקוח (אופציונלי)",
"internal_booking_note_description": "הערה זו תהיה גלויה רק לחברי הצוות שלך",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Miről szól ez a találkozó?",
"add_to_team": "Hozzáadás a csapathoz",
"remove_users_from_org": "Felhasználók eltávolítása a szervezetből",
"domain_wide_delegation": "Tartományra kiterjedő delegálás",
"domain_wide_delegation_description": "A tartományra kiterjedő delegálás lehetővé teszi a Google Workspace naptárak hozzáférésének kezelését a szervezet számára.",
"delegation_credential": "Tartományra kiterjedő delegálás",
"delegation_credential_description": "A tartományra kiterjedő delegálás lehetővé teszi a Google Workspace naptárak hozzáférésének kezelését a szervezet számára.",
"add_client_id_in_google_workspace_with_below_scope": "Adja hozzá ezt az ügyfél-azonosítót a Google Workspace-hez az alábbi hatókörrel",
"domain_wide_delegation_enabled": "Tartományra kiterjedő delegálás engedélyezve",
"domain_wide_delegation_disabled": "Tartományra kiterjedő delegálás letiltva",
"add_domain_wide_delegation": "Tartományra kiterjedő delegálás hozzáadása",
"edit_domain_wide_delegation": "Tartományra kiterjedő delegálás szerkesztése",
"delegation_credential_enabled": "Tartományra kiterjedő delegálás engedélyezve",
"delegation_credential_disabled": "Tartományra kiterjedő delegálás letiltva",
"add_delegation_credential": "Tartományra kiterjedő delegálás hozzáadása",
"edit_delegation_credential": "Tartományra kiterjedő delegálás szerkesztése",
"domain": "Tartomány",
"no_workspace_platforms": "Nincsenek workspace platformok",
"workspace_platform": "Workspace platform",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "Szolgáltatási fiók kulcs JSON",
"edit_service_account_key": "Szolgáltatási fiók kulcs JSON szerkesztése",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "A tartományra kiterjedő delegálás korlátozza egynél több telepítés hozzáadását",
"delegation_credential_restricts_adding_more_than_one_installation": "A tartományra kiterjedő delegálás korlátozza egynél több telepítés hozzáadását",
"app_successfully_installed_and_is_using_delegated_credentials": "Az alkalmazás sikeresen telepítve lett és delegált hitelesítő adatokat használ",
"remove_users_from_org_confirm": "Biztosan eltávolít {{userCount}} felhasználót ebből a szervezetből?",
"remove_users_from_team": "Felhasználók eltávolítása a csapatból",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Váratlan hiba történt. Kérjük, próbálja újra később",
"select_oAuth_client": "OAuth kliens kiválasztása",
"on_every_instance": "Minden példányon",
"domain_wide_delegation_feature_not_enabled": "A tartományra kiterjedő delegálás funkció nincs engedélyezve a szervezete számára",
"delegation_credential_feature_not_enabled": "A tartományra kiterjedő delegálás funkció nincs engedélyezve a szervezete számára",
"internal_booking_note": "Belső megjegyzés",
"internal_note_cancellation_reason": "Az ügyféllel megosztott indok (opcionális)",
"internal_booking_note_description": "Ez a megjegyzés csak a csapattagok számára lesz látható",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Qual è l'oggetto della riunione?",
"add_to_team": "Aggiungi al team",
"remove_users_from_org": "Rimuovi utenti dall'organizzazione",
"domain_wide_delegation": "Delega a livello di dominio",
"domain_wide_delegation_description": "La delega a livello di dominio ti permette di gestire l'accesso ai calendari di Google Workspace per la tua organizzazione.",
"delegation_credential": "Delega a livello di dominio",
"delegation_credential_description": "La delega a livello di dominio ti permette di gestire l'accesso ai calendari di Google Workspace per la tua organizzazione.",
"add_client_id_in_google_workspace_with_below_scope": "Aggiungi questo Client Id in Google Workspace con lo scope indicato sotto",
"domain_wide_delegation_enabled": "Delega a livello di dominio attivata",
"domain_wide_delegation_disabled": "Delega a livello di dominio disattivata",
"add_domain_wide_delegation": "Aggiungi delega a livello di dominio",
"edit_domain_wide_delegation": "Modifica delega a livello di dominio",
"delegation_credential_enabled": "Delega a livello di dominio attivata",
"delegation_credential_disabled": "Delega a livello di dominio disattivata",
"add_delegation_credential": "Aggiungi delega a livello di dominio",
"edit_delegation_credential": "Modifica delega a livello di dominio",
"domain": "Dominio",
"no_workspace_platforms": "Nessuna piattaforma workspace",
"workspace_platform": "Piattaforma workspace",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "JSON chiave account di servizio",
"edit_service_account_key": "Modifica JSON chiave account di servizio",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "La delega a livello di dominio limita l'aggiunta di più di un'installazione",
"delegation_credential_restricts_adding_more_than_one_installation": "La delega a livello di dominio limita l'aggiunta di più di un'installazione",
"app_successfully_installed_and_is_using_delegated_credentials": "App installata con successo e sta utilizzando le credenziali delegate",
"remove_users_from_org_confirm": "Rimuovere {{userCount}} utenti da questa organizzazione?",
"remove_users_from_team": "Rimuovi utenti dal team",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Si è verificato un errore imprevisto. Per favore, riprova più tardi",
"select_oAuth_client": "Seleziona client OAuth",
"on_every_instance": "Su ogni istanza",
"domain_wide_delegation_feature_not_enabled": "La funzione di delega a livello di dominio non è attivata per la tua organizzazione",
"delegation_credential_feature_not_enabled": "La funzione di delega a livello di dominio non è attivata per la tua organizzazione",
"internal_booking_note": "Nota interna",
"internal_note_cancellation_reason": "Motivo condiviso con il cliente (opzionale)",
"internal_booking_note_description": "Questa nota sarà visibile solo ai membri del tuo team",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "このミーティングの内容は?",
"add_to_team": "チームに追加",
"remove_users_from_org": "組織からユーザーを削除",
"domain_wide_delegation": "ドメイン全体の委任",
"domain_wide_delegation_description": "ドメイン全体の委任により、組織のGoogle Workspaceカレンダーへのアクセスを管理できます。",
"delegation_credential": "ドメイン全体の委任",
"delegation_credential_description": "ドメイン全体の委任により、組織のGoogle Workspaceカレンダーへのアクセスを管理できます。",
"add_client_id_in_google_workspace_with_below_scope": "以下のスコープを使用してGoogle WorkspaceにこのクライアントIDを追加してください",
"domain_wide_delegation_enabled": "ドメイン全体の委任が有効",
"domain_wide_delegation_disabled": "ドメイン全体の委任が無効",
"add_domain_wide_delegation": "ドメイン全体の委任を追加",
"edit_domain_wide_delegation": "ドメイン全体の委任を編集",
"delegation_credential_enabled": "ドメイン全体の委任が有効",
"delegation_credential_disabled": "ドメイン全体の委任が無効",
"add_delegation_credential": "ドメイン全体の委任を追加",
"edit_delegation_credential": "ドメイン全体の委任を編集",
"domain": "ドメイン",
"no_workspace_platforms": "ワークスペースプラットフォームがありません",
"workspace_platform": "ワークスペースプラットフォーム",
@@ -2287,7 +2287,7 @@
"slug": "スラッグ",
"service_account_key": "サービスアカウントキーJSON",
"edit_service_account_key": "サービスアカウントキーJSONを編集",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "ドメイン全体の委任では、複数のインストールを追加することはできません",
"delegation_credential_restricts_adding_more_than_one_installation": "ドメイン全体の委任では、複数のインストールを追加することはできません",
"app_successfully_installed_and_is_using_delegated_credentials": "アプリケーションが正常にインストールされ、委任された認証情報を使用しています",
"remove_users_from_org_confirm": "この組織から {{userCount}} 人のユーザーを削除しますか?",
"remove_users_from_team": "チームからユーザーを削除",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "予期せぬエラーが発生しました。後でもう一度お試しください",
"select_oAuth_client": "OAuthクライアントを選択",
"on_every_instance": "すべてのインスタンスで",
"domain_wide_delegation_feature_not_enabled": "ドメイン全体の委任機能が組織で有効になっていません",
"delegation_credential_feature_not_enabled": "ドメイン全体の委任機能が組織で有効になっていません",
"internal_booking_note": "内部メモ",
"internal_note_cancellation_reason": "顧客と共有する理由(任意)",
"internal_booking_note_description": "このメモはチームメンバーのみが閲覧できます",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "កិច្ចប្រជុំនេះគឺអំពីអ្វី?",
"add_to_team": "បន្ថែមទៅក្រុម",
"remove_users_from_org": "យកអ្នកប្រើចេញពីអង្គការ",
"domain_wide_delegation": "ការប្រគល់សិទ្ធិទូទាំងដែន",
"domain_wide_delegation_description": "ការប្រគល់សិទ្ធិទូទាំងដែនអនុញ្ញាតឱ្យអ្នកគ្រប់គ្រងការចូលប្រើប្រតិទិន Google Workspace សម្រាប់ស្ថាប័នរបស់អ្នក។",
"delegation_credential": "ការប្រគល់សិទ្ធិទូទាំងដែន",
"delegation_credential_description": "ការប្រគល់សិទ្ធិទូទាំងដែនអនុញ្ញាតឱ្យអ្នកគ្រប់គ្រងការចូលប្រើប្រតិទិន Google Workspace សម្រាប់ស្ថាប័នរបស់អ្នក។",
"add_client_id_in_google_workspace_with_below_scope": "បន្ថែម Client Id នេះនៅក្នុង Google Workspace ជាមួយនឹងវិសាលភាពខាងក្រោម",
"domain_wide_delegation_enabled": "បានបើកការប្រគល់សិទ្ធិទូទាំងដែន",
"domain_wide_delegation_disabled": "បានបិទការប្រគល់សិទ្ធិទូទាំងដែន",
"add_domain_wide_delegation": "បន្ថែមការប្រគល់សិទ្ធិទូទាំងដែន",
"edit_domain_wide_delegation": "កែសម្រួលការប្រគល់សិទ្ធិទូទាំងដែន",
"delegation_credential_enabled": "បានបើកការប្រគល់សិទ្ធិទូទាំងដែន",
"delegation_credential_disabled": "បានបិទការប្រគល់សិទ្ធិទូទាំងដែន",
"add_delegation_credential": "បន្ថែមការប្រគល់សិទ្ធិទូទាំងដែន",
"edit_delegation_credential": "កែសម្រួលការប្រគល់សិទ្ធិទូទាំងដែន",
"domain": "ដែន",
"no_workspace_platforms": "គ្មានវេទិកាការងារ",
"workspace_platform": "វេទិកាការងារ",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "កូនសោគណនីសេវាកម្ម JSON",
"edit_service_account_key": "កែសម្រួលកូនសោគណនីសេវាកម្ម JSON",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "ការប្រគល់សិទ្ធិទូទាំងដែនកំណត់ការបន្ថែមការដំឡើងច្រើនជាងមួយ",
"delegation_credential_restricts_adding_more_than_one_installation": "ការប្រគល់សិទ្ធិទូទាំងដែនកំណត់ការបន្ថែមការដំឡើងច្រើនជាងមួយ",
"app_successfully_installed_and_is_using_delegated_credentials": "កម្មវិធីត្រូវបានដំឡើងដោយជោគជ័យ និងកំពុងប្រើប្រាស់ព័ត៌មានសម្ងាត់ដែលបានប្រគល់សិទ្ធិ",
"remove_users_from_org_confirm": "តើអ្នកប្រាកដថាចង់យកអ្នកប្រើ {{userCount}} ចេញពីអង្គការនេះមែនទេ?",
"remove_users_from_team": "ដកអ្នកប្រើចេញពីក្រុម",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "មានកំហុសដែលមិនបានរំពឹងទុកកើតឡើង។ សូមព្យាយាមម្តងទៀតនៅពេលក្រោយ",
"select_oAuth_client": "ជ្រើសរើស OAuth Client",
"on_every_instance": "នៅលើគ្រប់ករណី",
"domain_wide_delegation_feature_not_enabled": "មុខងារប្រគល់សិទ្ធិទូទាំងដែនមិនត្រូវបានបើកសម្រាប់ស្ថាប័នរបស់អ្នកទេ",
"delegation_credential_feature_not_enabled": "មុខងារប្រគល់សិទ្ធិទូទាំងដែនមិនត្រូវបានបើកសម្រាប់ស្ថាប័នរបស់អ្នកទេ",
"internal_booking_note": "កំណត់ចំណាំផ្ទៃក្នុង",
"internal_note_cancellation_reason": "មូលហេតុដែលត្រូវចែករំលែកជាមួយអតិថិជន (ជាជម្រើស)",
"internal_booking_note_description": "កំណត់ចំណាំនេះនឹងអាចមើលឃើញសម្រាប់តែសមាជិកក្រុមរបស់អ្នកប៉ុណ្ណោះ",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "이 회의는 어떤 주제를 다루나요?",
"add_to_team": "팀에 추가",
"remove_users_from_org": "조직에서 사용자 제거",
"domain_wide_delegation": "도메인 전체 위임",
"domain_wide_delegation_description": "도메인 전체 위임을 통해 조직의 Google Workspace 캘린더에 대한 액세스를 관리할 수 있습니다.",
"delegation_credential": "도메인 전체 위임",
"delegation_credential_description": "도메인 전체 위임을 통해 조직의 Google Workspace 캘린더에 대한 액세스를 관리할 수 있습니다.",
"add_client_id_in_google_workspace_with_below_scope": "아래 범위로 Google Workspace에 이 클라이언트 ID를 추가하세요",
"domain_wide_delegation_enabled": "도메인 전체 위임이 활성화됨",
"domain_wide_delegation_disabled": "도메인 전체 위임이 비활성화됨",
"add_domain_wide_delegation": "도메인 전체 위임 추가",
"edit_domain_wide_delegation": "도메인 전체 위임 수정",
"delegation_credential_enabled": "도메인 전체 위임이 활성화됨",
"delegation_credential_disabled": "도메인 전체 위임이 비활성화됨",
"add_delegation_credential": "도메인 전체 위임 추가",
"edit_delegation_credential": "도메인 전체 위임 수정",
"domain": "도메인",
"no_workspace_platforms": "워크스페이스 플랫폼 없음",
"workspace_platform": "워크스페이스 플랫폼",
@@ -2287,7 +2287,7 @@
"slug": "슬러그",
"service_account_key": "서비스 계정 키 JSON",
"edit_service_account_key": "서비스 계정 키 JSON 수정",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "도메인 전체 위임은 두 개 이상의 설치를 제한합니다",
"delegation_credential_restricts_adding_more_than_one_installation": "도메인 전체 위임은 두 개 이상의 설치를 제한합니다",
"app_successfully_installed_and_is_using_delegated_credentials": "앱이 성공적으로 설치되었으며 위임된 자격 증명을 사용 중입니다",
"remove_users_from_org_confirm": "이 조직에서 {{userCount}}명의 사용자를 제거하시겠습니까?",
"remove_users_from_team": "팀에서 사용자 제거",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "예기치 않은 오류가 발생했습니다. 나중에 다시 시도해 주세요",
"select_oAuth_client": "OAuth 클라이언트 선택",
"on_every_instance": "모든 인스턴스에서",
"domain_wide_delegation_feature_not_enabled": "도메인 전체 위임 기능이 귀하의 조직에 대해 활성화되어 있지 않습니다",
"delegation_credential_feature_not_enabled": "도메인 전체 위임 기능이 귀하의 조직에 대해 활성화되어 있지 않습니다",
"internal_booking_note": "내부 메모",
"internal_note_cancellation_reason": "고객과 공유할 사유 (선택사항)",
"internal_booking_note_description": "이 메모는 팀원들에게만 표시됩니다",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Waar gaat deze afspraak over?",
"add_to_team": "Voeg toe aan team",
"remove_users_from_org": "Gebruikers verwijderen uit organisatie",
"domain_wide_delegation": "Domeinbrede delegatie",
"domain_wide_delegation_description": "Domeinbrede delegatie stelt je in staat om toegang tot Google Workspace-agenda's voor je organisatie te beheren.",
"delegation_credential": "Domeinbrede delegatie",
"delegation_credential_description": "Domeinbrede delegatie stelt je in staat om toegang tot Google Workspace-agenda's voor je organisatie te beheren.",
"add_client_id_in_google_workspace_with_below_scope": "Voeg deze Client ID toe in Google Workspace met onderstaande scope",
"domain_wide_delegation_enabled": "Domeinbrede delegatie ingeschakeld",
"domain_wide_delegation_disabled": "Domeinbrede delegatie uitgeschakeld",
"add_domain_wide_delegation": "Domeinbrede delegatie toevoegen",
"edit_domain_wide_delegation": "Domeinbrede delegatie bewerken",
"delegation_credential_enabled": "Domeinbrede delegatie ingeschakeld",
"delegation_credential_disabled": "Domeinbrede delegatie uitgeschakeld",
"add_delegation_credential": "Domeinbrede delegatie toevoegen",
"edit_delegation_credential": "Domeinbrede delegatie bewerken",
"domain": "Domein",
"no_workspace_platforms": "Geen workspace-platforms",
"workspace_platform": "Workspace-platform",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "Serviceaccount-sleutel JSON",
"edit_service_account_key": "Serviceaccount-sleutel JSON bewerken",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Domeinbrede delegatie beperkt het toevoegen van meer dan één installatie",
"delegation_credential_restricts_adding_more_than_one_installation": "Domeinbrede delegatie beperkt het toevoegen van meer dan één installatie",
"app_successfully_installed_and_is_using_delegated_credentials": "App succesvol geïnstalleerd en gebruikt gedelegeerde inloggegevens",
"remove_users_from_org_confirm": "Weet u zeker dat u {{userCount}} gebruikers uit deze organisatie wilt verwijderen?",
"remove_users_from_team": "Gebruikers uit team verwijderen",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Er is een onverwachte fout opgetreden. Probeer het later opnieuw",
"select_oAuth_client": "Selecteer OAuth-client",
"on_every_instance": "Op elke instantie",
"domain_wide_delegation_feature_not_enabled": "De functie voor domeinbrede delegatie is niet ingeschakeld voor jouw organisatie",
"delegation_credential_feature_not_enabled": "De functie voor domeinbrede delegatie is niet ingeschakeld voor jouw organisatie",
"internal_booking_note": "Interne notitie",
"internal_note_cancellation_reason": "Reden gedeeld met klant (optioneel)",
"internal_booking_note_description": "Deze notitie is alleen zichtbaar voor leden van je team",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Hva handler dette møtet om?",
"add_to_team": "Legg til i team",
"remove_users_from_org": "Fjern brukere fra organisasjonen",
"domain_wide_delegation": "Domeneomfattende delegering",
"domain_wide_delegation_description": "Domeneomfattende delegering lar deg administrere tilgang til Google Workspace-kalendere for din organisasjon.",
"delegation_credential": "Domeneomfattende delegering",
"delegation_credential_description": "Domeneomfattende delegering lar deg administrere tilgang til Google Workspace-kalendere for din organisasjon.",
"add_client_id_in_google_workspace_with_below_scope": "Legg til denne klient-ID-en i Google Workspace med følgende omfang",
"domain_wide_delegation_enabled": "Domeneomfattende delegering er aktivert",
"domain_wide_delegation_disabled": "Domeneomfattende delegering er deaktivert",
"add_domain_wide_delegation": "Legg til domeneomfattende delegering",
"edit_domain_wide_delegation": "Rediger domeneomfattende delegering",
"delegation_credential_enabled": "Domeneomfattende delegering er aktivert",
"delegation_credential_disabled": "Domeneomfattende delegering er deaktivert",
"add_delegation_credential": "Legg til domeneomfattende delegering",
"edit_delegation_credential": "Rediger domeneomfattende delegering",
"domain": "Domene",
"no_workspace_platforms": "Ingen arbeidsplattformer",
"workspace_platform": "Arbeidsplattform",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "JSON-nøkkel for tjenestekonto",
"edit_service_account_key": "Rediger JSON-nøkkel for tjenestekonto",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Domeneomfattende delegering begrenser til kun én installasjon",
"delegation_credential_restricts_adding_more_than_one_installation": "Domeneomfattende delegering begrenser til kun én installasjon",
"app_successfully_installed_and_is_using_delegated_credentials": "Appen er installert og bruker delegerte påloggingsdetaljer",
"remove_users_from_org_confirm": "Er du sikker på at du vil fjerne {{userCount}} brukere fra denne organisasjonen?",
"remove_users_from_team": "Fjern brukere fra teamet",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "En uventet feil oppstod. Vennligst prøv igjen senere",
"select_oAuth_client": "Velg OAuth-klient",
"on_every_instance": "På hver instans",
"domain_wide_delegation_feature_not_enabled": "Funksjonen for domeneomfattende delegering er ikke aktivert for din organisasjon",
"delegation_credential_feature_not_enabled": "Funksjonen for domeneomfattende delegering er ikke aktivert for din organisasjon",
"internal_booking_note": "Internt notat",
"internal_note_cancellation_reason": "Årsak som deles med kunden (valgfritt)",
"internal_booking_note_description": "Dette notatet vil kun være synlig for medlemmer av teamet ditt",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Czego dotyczy to spotkanie?",
"add_to_team": "Dodaj do zespołu",
"remove_users_from_org": "Usuń użytkowników z organizacji",
"domain_wide_delegation": "Delegacja na poziomie domeny",
"domain_wide_delegation_description": "Delegacja na poziomie domeny pozwala zarządzać dostępem do kalendarzy Google Workspace dla Twojej organizacji.",
"delegation_credential": "Delegacja na poziomie domeny",
"delegation_credential_description": "Delegacja na poziomie domeny pozwala zarządzać dostępem do kalendarzy Google Workspace dla Twojej organizacji.",
"add_client_id_in_google_workspace_with_below_scope": "Dodaj ten identyfikator klienta w Google Workspace z poniższym zakresem",
"domain_wide_delegation_enabled": "Delegacja na poziomie domeny włączona",
"domain_wide_delegation_disabled": "Delegacja na poziomie domeny wyłączona",
"add_domain_wide_delegation": "Dodaj delegację na poziomie domeny",
"edit_domain_wide_delegation": "Edytuj delegację na poziomie domeny",
"delegation_credential_enabled": "Delegacja na poziomie domeny włączona",
"delegation_credential_disabled": "Delegacja na poziomie domeny wyłączona",
"add_delegation_credential": "Dodaj delegację na poziomie domeny",
"edit_delegation_credential": "Edytuj delegację na poziomie domeny",
"domain": "Domena",
"no_workspace_platforms": "Brak platform workspace",
"workspace_platform": "Platforma workspace",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "Klucz JSON konta usługi",
"edit_service_account_key": "Edytuj klucz JSON konta usługi",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Delegacja na poziomie domeny ogranicza dodawanie więcej niż jednej instalacji",
"delegation_credential_restricts_adding_more_than_one_installation": "Delegacja na poziomie domeny ogranicza dodawanie więcej niż jednej instalacji",
"app_successfully_installed_and_is_using_delegated_credentials": "Aplikacja została pomyślnie zainstalowana i używa poświadczeń delegowanych",
"remove_users_from_org_confirm": "Czy na pewno chcesz usunąć {{userCount}} użytkowników z tej organizacji?",
"remove_users_from_team": "Usuń użytkowników z zespołu",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Wystąpił nieoczekiwany błąd. Spróbuj ponownie później",
"select_oAuth_client": "Wybierz klienta OAuth",
"on_every_instance": "W każdej instancji",
"domain_wide_delegation_feature_not_enabled": "Funkcja delegacji na poziomie domeny nie jest włączona dla Twojej organizacji",
"delegation_credential_feature_not_enabled": "Funkcja delegacji na poziomie domeny nie jest włączona dla Twojej organizacji",
"internal_booking_note": "Notatka wewnętrzna",
"internal_note_cancellation_reason": "Powód udostępniony klientowi (opcjonalnie)",
"internal_booking_note_description": "Ta notatka będzie widoczna tylko dla członków twojego zespołu",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "De que se trata esta reunião?",
"add_to_team": "Adicionar à equipe",
"remove_users_from_org": "Remover usuários da organização",
"domain_wide_delegation": "Delegação em todo o domínio",
"domain_wide_delegation_description": "A delegação em todo o domínio permite que você gerencie o acesso aos calendários do Google Workspace para sua organização.",
"delegation_credential": "Delegação em todo o domínio",
"delegation_credential_description": "A delegação em todo o domínio permite que você gerencie o acesso aos calendários do Google Workspace para sua organização.",
"add_client_id_in_google_workspace_with_below_scope": "Adicione este ID do Cliente no Google Workspace com o escopo abaixo",
"domain_wide_delegation_enabled": "Delegação em todo o domínio ativada",
"domain_wide_delegation_disabled": "Delegação em todo o domínio desativada",
"add_domain_wide_delegation": "Adicionar delegação em todo o domínio",
"edit_domain_wide_delegation": "Editar delegação em todo o domínio",
"delegation_credential_enabled": "Delegação em todo o domínio ativada",
"delegation_credential_disabled": "Delegação em todo o domínio desativada",
"add_delegation_credential": "Adicionar delegação em todo o domínio",
"edit_delegation_credential": "Editar delegação em todo o domínio",
"domain": "Domínio",
"no_workspace_platforms": "Nenhuma plataforma de workspace",
"workspace_platform": "Plataforma de workspace",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "JSON da chave da conta de serviço",
"edit_service_account_key": "Editar JSON da chave da conta de serviço",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "A delegação em todo o domínio restringe a adição de mais de uma instalação",
"delegation_credential_restricts_adding_more_than_one_installation": "A delegação em todo o domínio restringe a adição de mais de uma instalação",
"app_successfully_installed_and_is_using_delegated_credentials": "Aplicativo instalado com sucesso e está usando credenciais delegadas",
"remove_users_from_org_confirm": "Tem certeza de que deseja remover {{userCount}} usuários desta organização?",
"remove_users_from_team": "Remover usuários da equipe",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Ocorreu um erro inesperado. Por favor, tente novamente mais tarde",
"select_oAuth_client": "Selecionar cliente OAuth",
"on_every_instance": "Em cada instância",
"domain_wide_delegation_feature_not_enabled": "O recurso de delegação em todo o domínio não está ativado para sua organização",
"delegation_credential_feature_not_enabled": "O recurso de delegação em todo o domínio não está ativado para sua organização",
"internal_booking_note": "Nota interna",
"internal_note_cancellation_reason": "Motivo compartilhado com o cliente (opcional)",
"internal_booking_note_description": "Esta nota será visível apenas para membros da sua equipe",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Sobre o que é esta reunião?",
"add_to_team": "Adicionar à equipa",
"remove_users_from_org": "Remover utilizadores da organização",
"domain_wide_delegation": "Delegação em todo o domínio",
"domain_wide_delegation_description": "A delegação em todo o domínio permite que você gerencie o acesso aos calendários do Google Workspace para sua organização.",
"delegation_credential": "Delegação em todo o domínio",
"delegation_credential_description": "A delegação em todo o domínio permite que você gerencie o acesso aos calendários do Google Workspace para sua organização.",
"add_client_id_in_google_workspace_with_below_scope": "Adicione este ID do Cliente no Google Workspace com o escopo abaixo",
"domain_wide_delegation_enabled": "Delegação em todo o domínio ativada",
"domain_wide_delegation_disabled": "Delegação em todo o domínio desativada",
"add_domain_wide_delegation": "Adicionar delegação em todo o domínio",
"edit_domain_wide_delegation": "Editar delegação em todo o domínio",
"delegation_credential_enabled": "Delegação em todo o domínio ativada",
"delegation_credential_disabled": "Delegação em todo o domínio desativada",
"add_delegation_credential": "Adicionar delegação em todo o domínio",
"edit_delegation_credential": "Editar delegação em todo o domínio",
"domain": "Domínio",
"no_workspace_platforms": "Nenhuma plataforma de workspace",
"workspace_platform": "Plataforma de workspace",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "JSON da chave da conta de serviço",
"edit_service_account_key": "Editar JSON da chave da conta de serviço",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "A delegação em todo o domínio restringe a adição de mais de uma instalação",
"delegation_credential_restricts_adding_more_than_one_installation": "A delegação em todo o domínio restringe a adição de mais de uma instalação",
"app_successfully_installed_and_is_using_delegated_credentials": "Aplicativo instalado com sucesso e está usando credenciais delegadas",
"remove_users_from_org_confirm": "Tem a certeza de que deseja remover {{userCount}} utilizadores desta organização?",
"remove_users_from_team": "Remover utilizadores da equipa",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Ocorreu um erro inesperado. Por favor, tente novamente mais tarde",
"select_oAuth_client": "Selecionar cliente OAuth",
"on_every_instance": "Em cada instância",
"domain_wide_delegation_feature_not_enabled": "O recurso de delegação em todo o domínio não está ativado para sua organização",
"delegation_credential_feature_not_enabled": "O recurso de delegação em todo o domínio não está ativado para sua organização",
"internal_booking_note": "Nota interna",
"internal_note_cancellation_reason": "Motivo compartilhado com o cliente (opcional)",
"internal_booking_note_description": "Esta nota será visível apenas para os membros da sua equipe",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Despre ce este vorba în această ședință?",
"add_to_team": "Adăugați la echipă",
"remove_users_from_org": "Eliminați utilizatori din organizație",
"domain_wide_delegation": "Delegare la nivel de domeniu",
"domain_wide_delegation_description": "Delegarea la nivel de domeniu îți permite să gestionezi accesul la calendarele Google Workspace pentru organizația ta.",
"delegation_credential": "Delegare la nivel de domeniu",
"delegation_credential_description": "Delegarea la nivel de domeniu îți permite să gestionezi accesul la calendarele Google Workspace pentru organizația ta.",
"add_client_id_in_google_workspace_with_below_scope": "Adaugă acest Client Id în Google Workspace cu scope-ul de mai jos",
"domain_wide_delegation_enabled": "Delegare la nivel de domeniu activată",
"domain_wide_delegation_disabled": "Delegare la nivel de domeniu dezactivată",
"add_domain_wide_delegation": "Adaugă delegare la nivel de domeniu",
"edit_domain_wide_delegation": "Editează delegarea la nivel de domeniu",
"delegation_credential_enabled": "Delegare la nivel de domeniu activată",
"delegation_credential_disabled": "Delegare la nivel de domeniu dezactivată",
"add_delegation_credential": "Adaugă delegare la nivel de domeniu",
"edit_delegation_credential": "Editează delegarea la nivel de domeniu",
"domain": "Domeniu",
"no_workspace_platforms": "Nu există platforme workspace",
"workspace_platform": "Platformă workspace",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "Cheie JSON cont de serviciu",
"edit_service_account_key": "Editează cheia JSON a contului de serviciu",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Delegarea la nivel de domeniu restricționează adăugarea mai multor instalări",
"delegation_credential_restricts_adding_more_than_one_installation": "Delegarea la nivel de domeniu restricționează adăugarea mai multor instalări",
"app_successfully_installed_and_is_using_delegated_credentials": "Aplicația a fost instalată cu succes și folosește credențiale delegate",
"remove_users_from_org_confirm": "Sigur doriți să eliminați {{userCount}} utilizatori din această organizație?",
"remove_users_from_team": "Elimină utilizatorii din echipă",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "A apărut o eroare neașteptată. Te rugăm să încerci din nou mai târziu",
"select_oAuth_client": "Selectează clientul OAuth",
"on_every_instance": "La fiecare instanță",
"domain_wide_delegation_feature_not_enabled": "Funcția de delegare la nivel de domeniu nu este activată pentru organizația ta",
"delegation_credential_feature_not_enabled": "Funcția de delegare la nivel de domeniu nu este activată pentru organizația ta",
"internal_booking_note": "Notă internă",
"internal_note_cancellation_reason": "Motivul comunicat clientului (opțional)",
"internal_booking_note_description": "Această notă va fi vizibilă doar membrilor echipei tale",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Чему посвязена эта встреча?",
"add_to_team": "Добавить в команду",
"remove_users_from_org": "Удалить пользователей из организации",
"domain_wide_delegation": "Делегирование на уровне домена",
"domain_wide_delegation_description": "Делегирование на уровне домена позволяет управлять доступом к календарям Google Workspace для вашей организации.",
"delegation_credential": "Делегирование на уровне домена",
"delegation_credential_description": "Делегирование на уровне домена позволяет управлять доступом к календарям Google Workspace для вашей организации.",
"add_client_id_in_google_workspace_with_below_scope": "Добавьте этот Client Id в Google Workspace с указанной ниже областью доступа",
"domain_wide_delegation_enabled": "Делегирование на уровне домена включено",
"domain_wide_delegation_disabled": "Делегирование на уровне домена отключено",
"add_domain_wide_delegation": "Добавить делегирование на уровне домена",
"edit_domain_wide_delegation": "Редактировать делегирование на уровне домена",
"delegation_credential_enabled": "Делегирование на уровне домена включено",
"delegation_credential_disabled": "Делегирование на уровне домена отключено",
"add_delegation_credential": "Добавить делегирование на уровне домена",
"edit_delegation_credential": "Редактировать делегирование на уровне домена",
"domain": "Домен",
"no_workspace_platforms": "Нет платформ workspace",
"workspace_platform": "Платформа workspace",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "JSON ключ сервисного аккаунта",
"edit_service_account_key": "Редактировать JSON ключ сервисного аккаунта",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Делегирование на уровне домена ограничивает добавление более одной установки",
"delegation_credential_restricts_adding_more_than_one_installation": "Делегирование на уровне домена ограничивает добавление более одной установки",
"app_successfully_installed_and_is_using_delegated_credentials": "Приложение успешно установлено и использует делегированные учетные данные",
"remove_users_from_org_confirm": "Удалить {{userCount}} пользователей из этой организации?",
"remove_users_from_team": "Удалить пользователей из команды",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Произошла непредвиденная ошибка. Пожалуйста, попробуйте позже",
"select_oAuth_client": "Выберите OAuth-клиент",
"on_every_instance": "На каждом экземпляре",
"domain_wide_delegation_feature_not_enabled": "Функция делегирования на уровне домена не включена для вашей организации",
"delegation_credential_feature_not_enabled": "Функция делегирования на уровне домена не включена для вашей организации",
"internal_booking_note": "Внутренняя заметка",
"internal_note_cancellation_reason": "Причина, которая будет показана клиенту (необязательно)",
"internal_booking_note_description": "Эта заметка будет видна только участникам вашей команды",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "O čom je toto stretnutie?",
"add_to_team": "Pridať do tímu",
"remove_users_from_org": "Odstrániť používateľov z organizácie",
"domain_wide_delegation": "Delegovanie na úrovni domény",
"domain_wide_delegation_description": "Delegovanie na úrovni domény vám umožňuje spravovať prístup ku kalendárom Google Workspace pre vašu organizáciu.",
"delegation_credential": "Delegovanie na úrovni domény",
"delegation_credential_description": "Delegovanie na úrovni domény vám umožňuje spravovať prístup ku kalendárom Google Workspace pre vašu organizáciu.",
"add_client_id_in_google_workspace_with_below_scope": "Pridajte toto Client Id do Google Workspace s nasledujúcim rozsahom",
"domain_wide_delegation_enabled": "Delegovanie na úrovni domény je povolené",
"domain_wide_delegation_disabled": "Delegovanie na úrovni domény je zakázané",
"add_domain_wide_delegation": "Pridať delegovanie na úrovni domény",
"edit_domain_wide_delegation": "Upraviť delegovanie na úrovni domény",
"delegation_credential_enabled": "Delegovanie na úrovni domény je povolené",
"delegation_credential_disabled": "Delegovanie na úrovni domény je zakázané",
"add_delegation_credential": "Pridať delegovanie na úrovni domény",
"edit_delegation_credential": "Upraviť delegovanie na úrovni domény",
"domain": "Doména",
"no_workspace_platforms": "Žiadne workspace platformy",
"workspace_platform": "Workspace platforma",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "Service account key JSON",
"edit_service_account_key": "Upraviť service account key JSON",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Delegovanie na úrovni domény obmedzuje pridanie viac ako jednej inštalácie",
"delegation_credential_restricts_adding_more_than_one_installation": "Delegovanie na úrovni domény obmedzuje pridanie viac ako jednej inštalácie",
"app_successfully_installed_and_is_using_delegated_credentials": "Aplikácia bola úspešne nainštalovaná a používa delegované prístupové údaje",
"remove_users_from_org_confirm": "Naozaj chcete odstrániť {{userCount}} používateľov z tejto organizácie?",
"remove_users_from_team": "Odstrániť používateľov z tímu",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Vyskytla sa neočakávaná chyba. Skúste to prosím neskôr znova",
"select_oAuth_client": "Vyberte OAuth klienta",
"on_every_instance": "Pri každej inštancii",
"domain_wide_delegation_feature_not_enabled": "Funkcia delegovania na úrovni domény nie je pre vašu organizáciu povolená",
"delegation_credential_feature_not_enabled": "Funkcia delegovania na úrovni domény nie je pre vašu organizáciu povolená",
"internal_booking_note": "Interná poznámka",
"internal_note_cancellation_reason": "Dôvod zdieľaný so zákazníkom (voliteľné)",
"internal_booking_note_description": "Táto poznámka bude viditeľná len pre členov vášho tímu",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "O čemu je sastanak?",
"add_to_team": "Dodajte timu",
"remove_users_from_org": "Uklonite korisnike iz organizacije",
"domain_wide_delegation": "Delegiranje na nivou domena",
"domain_wide_delegation_description": "Delegiranje na nivou domena vam omogućava da upravljate pristupom Google Workspace kalendarima za vašu organizaciju.",
"delegation_credential": "Delegiranje na nivou domena",
"delegation_credential_description": "Delegiranje na nivou domena vam omogućava da upravljate pristupom Google Workspace kalendarima za vašu organizaciju.",
"add_client_id_in_google_workspace_with_below_scope": "Dodajte ovaj Client Id u Google Workspace sa sledećim opsegom",
"domain_wide_delegation_enabled": "Delegiranje na nivou domena je omogućeno",
"domain_wide_delegation_disabled": "Delegiranje na nivou domena je onemogućeno",
"add_domain_wide_delegation": "Dodaj delegiranje na nivou domena",
"edit_domain_wide_delegation": "Izmeni delegiranje na nivou domena",
"delegation_credential_enabled": "Delegiranje na nivou domena je omogućeno",
"delegation_credential_disabled": "Delegiranje na nivou domena je onemogućeno",
"add_delegation_credential": "Dodaj delegiranje na nivou domena",
"edit_delegation_credential": "Izmeni delegiranje na nivou domena",
"domain": "Domen",
"no_workspace_platforms": "Nema workspace platformi",
"workspace_platform": "Workspace platforma",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "JSON ključ servisnog naloga",
"edit_service_account_key": "Izmeni JSON ključ servisnog naloga",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Delegiranje na nivou domena ograničava dodavanje više od jedne instalacije",
"delegation_credential_restricts_adding_more_than_one_installation": "Delegiranje na nivou domena ograničava dodavanje više od jedne instalacije",
"app_successfully_installed_and_is_using_delegated_credentials": "Aplikacija je uspešno instalirana i koristi delegirane kredencijale",
"remove_users_from_org_confirm": "Da li ste sigurni da želite da uklonite {{userCount}} korisnika iz organizacije?",
"remove_users_from_team": "Ukloni korisnike iz tima",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Došlo je do neočekivane greške. Molimo pokušajte ponovo kasnije",
"select_oAuth_client": "Izaberite OAuth klijenta",
"on_every_instance": "Na svakoj instanci",
"domain_wide_delegation_feature_not_enabled": "Funkcija delegiranja na nivou domena nije omogućena za vašu organizaciju",
"delegation_credential_feature_not_enabled": "Funkcija delegiranja na nivou domena nije omogućena za vašu organizaciju",
"internal_booking_note": "Interna beleška",
"internal_note_cancellation_reason": "Razlog koji se deli sa klijentom (opciono)",
"internal_booking_note_description": "Ova beleška će biti vidljiva samo članovima vašeg tima",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Vad handlar det här mötet om?",
"add_to_team": "Lägg till i teamet",
"remove_users_from_org": "Ta bort användare från organisationen",
"domain_wide_delegation": "Domänomfattande delegering",
"domain_wide_delegation_description": "Domänomfattande delegering låter dig hantera åtkomst till Google Workspace-kalendrar för din organisation.",
"delegation_credential": "Domänomfattande delegering",
"delegation_credential_description": "Domänomfattande delegering låter dig hantera åtkomst till Google Workspace-kalendrar för din organisation.",
"add_client_id_in_google_workspace_with_below_scope": "Lägg till detta klient-ID i Google Workspace med omfattningen nedan",
"domain_wide_delegation_enabled": "Domänomfattande delegering aktiverad",
"domain_wide_delegation_disabled": "Domänomfattande delegering inaktiverad",
"add_domain_wide_delegation": "Lägg till domänomfattande delegering",
"edit_domain_wide_delegation": "Redigera domänomfattande delegering",
"delegation_credential_enabled": "Domänomfattande delegering aktiverad",
"delegation_credential_disabled": "Domänomfattande delegering inaktiverad",
"add_delegation_credential": "Lägg till domänomfattande delegering",
"edit_delegation_credential": "Redigera domänomfattande delegering",
"domain": "Domän",
"no_workspace_platforms": "Inga workspace-plattformar",
"workspace_platform": "Workspace-plattform",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "JSON-nyckel för servicekonto",
"edit_service_account_key": "Redigera JSON-nyckel för servicekonto",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Domänomfattande delegering begränsar till max en installation",
"delegation_credential_restricts_adding_more_than_one_installation": "Domänomfattande delegering begränsar till max en installation",
"app_successfully_installed_and_is_using_delegated_credentials": "Appen har installerats och använder delegerade autentiseringsuppgifter",
"remove_users_from_org_confirm": "Är du säker på att du vill ta bort {{userCount}} användare från denna organisation?",
"remove_users_from_team": "Ta bort användare från teamet",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Ett oväntat fel inträffade. Vänligen försök igen senare",
"select_oAuth_client": "Välj OAuth-klient",
"on_every_instance": "På varje instans",
"domain_wide_delegation_feature_not_enabled": "Funktionen för domänomfattande delegering är inte aktiverad för din organisation",
"delegation_credential_feature_not_enabled": "Funktionen för domänomfattande delegering är inte aktiverad för din organisation",
"internal_booking_note": "Intern anteckning",
"internal_note_cancellation_reason": "Orsak som delas med kunden (valfritt)",
"internal_booking_note_description": "Denna anteckning kommer endast att vara synlig för medlemmar i ditt team",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Bu toplantının konusu nedir?",
"add_to_team": "Ekibe ekle",
"remove_users_from_org": "Kullanıcıları gruptan kaldır",
"domain_wide_delegation": "Alan adı genelinde yetkilendirme",
"domain_wide_delegation_description": "Alan adı genelinde yetkilendirme, organizasyonunuz için Google Workspace takvimlerine erişimi yönetmenizi sağlar.",
"delegation_credential": "Alan adı genelinde yetkilendirme",
"delegation_credential_description": "Alan adı genelinde yetkilendirme, organizasyonunuz için Google Workspace takvimlerine erişimi yönetmenizi sağlar.",
"add_client_id_in_google_workspace_with_below_scope": "Bu İstemci Kimliğini aşağıdaki kapsam ile Google Workspace'e ekleyin",
"domain_wide_delegation_enabled": "Alan adı genelinde yetkilendirme etkin",
"domain_wide_delegation_disabled": "Alan adı genelinde yetkilendirme devre dışı",
"add_domain_wide_delegation": "Alan adı genelinde yetkilendirme ekle",
"edit_domain_wide_delegation": "Alan adı genelinde yetkilendirmeyi düzenle",
"delegation_credential_enabled": "Alan adı genelinde yetkilendirme etkin",
"delegation_credential_disabled": "Alan adı genelinde yetkilendirme devre dışı",
"add_delegation_credential": "Alan adı genelinde yetkilendirme ekle",
"edit_delegation_credential": "Alan adı genelinde yetkilendirmeyi düzenle",
"domain": "Alan adı",
"no_workspace_platforms": "Çalışma alanı platformu yok",
"workspace_platform": "Çalışma alanı platformu",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "Hizmet hesabı anahtarı JSON",
"edit_service_account_key": "Hizmet hesabı anahtarı JSON'ı düzenle",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Alan adı genelinde yetkilendirme, birden fazla kurulum eklemeyi kısıtlar",
"delegation_credential_restricts_adding_more_than_one_installation": "Alan adı genelinde yetkilendirme, birden fazla kurulum eklemeyi kısıtlar",
"app_successfully_installed_and_is_using_delegated_credentials": "Uygulama başarıyla kuruldu ve yetkilendirilmiş kimlik bilgilerini kullanıyor",
"remove_users_from_org_confirm": "{{userCount}} kullanıcıyı bu gruptan kaldırmak istediğinizden emin misiniz?",
"remove_users_from_team": "Kullanıcıları takımdan çıkar",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Beklenmeyen bir hata oluştu. Lütfen daha sonra tekrar deneyin",
"select_oAuth_client": "OAuth istemcisi seçin",
"on_every_instance": "Her örnekte",
"domain_wide_delegation_feature_not_enabled": "Alan adı genelinde yetkilendirme özelliği organizasyonunuz için etkinleştirilmemiş",
"delegation_credential_feature_not_enabled": "Alan adı genelinde yetkilendirme özelliği organizasyonunuz için etkinleştirilmemiş",
"internal_booking_note": "İç Not",
"internal_note_cancellation_reason": "Müşteri ile paylaşılacak neden (isteğe bağlı)",
"internal_booking_note_description": "Bu not yalnızca ekip üyeleriniz tarafından görülebilecektir",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Про що ця зустріч?",
"add_to_team": "Додати в команду",
"remove_users_from_org": "Видалити користувачів з організації",
"domain_wide_delegation": "Делегування на рівні домену",
"domain_wide_delegation_description": "Делегування на рівні домену дозволяє керувати доступом до календарів Google Workspace для вашої організації.",
"delegation_credential": "Делегування на рівні домену",
"delegation_credential_description": "Делегування на рівні домену дозволяє керувати доступом до календарів Google Workspace для вашої організації.",
"add_client_id_in_google_workspace_with_below_scope": "Додайте цей Client Id в Google Workspace з наведеною нижче областю дії",
"domain_wide_delegation_enabled": "Делегування на рівні домену увімкнено",
"domain_wide_delegation_disabled": "Делегування на рівні домену вимкнено",
"add_domain_wide_delegation": "Додати делегування на рівні домену",
"edit_domain_wide_delegation": "Редагувати делегування на рівні домену",
"delegation_credential_enabled": "Делегування на рівні домену увімкнено",
"delegation_credential_disabled": "Делегування на рівні домену вимкнено",
"add_delegation_credential": "Додати делегування на рівні домену",
"edit_delegation_credential": "Редагувати делегування на рівні домену",
"domain": "Домен",
"no_workspace_platforms": "Немає платформ workspace",
"workspace_platform": "Платформа workspace",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "JSON ключ сервісного акаунта",
"edit_service_account_key": "Редагувати JSON ключ сервісного акаунта",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Делегування на рівні домену обмежує додавання більше однієї інсталяції",
"delegation_credential_restricts_adding_more_than_one_installation": "Делегування на рівні домену обмежує додавання більше однієї інсталяції",
"app_successfully_installed_and_is_using_delegated_credentials": "Застосунок успішно встановлено і використовує делеговані облікові дані",
"remove_users_from_org_confirm": "Справді видалити користувачів ({{userCount}}) із цієї організації?",
"remove_users_from_team": "Видалити користувачів з команди",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Сталася неочікувана помилка. Будь ласка, спробуйте пізніше",
"select_oAuth_client": "Оберіть OAuth-клієнта",
"on_every_instance": "На кожному екземплярі",
"domain_wide_delegation_feature_not_enabled": "Функція делегування на рівні домену не увімкнена для вашої організації",
"delegation_credential_feature_not_enabled": "Функція делегування на рівні домену не увімкнена для вашої організації",
"internal_booking_note": "Внутрішня примітка",
"internal_note_cancellation_reason": "Причина, яка буде повідомлена клієнту (необов'язково)",
"internal_booking_note_description": "Ця примітка буде видима лише для учасників вашої команди",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "Cuộc họp này về điều gì?",
"add_to_team": "Thêm vào nhóm",
"remove_users_from_org": "Xoá bỏ người dùng khỏi tổ chức",
"domain_wide_delegation": "Ủy quyền toàn miền",
"domain_wide_delegation_description": "Ủy quyền toàn miền cho phép bạn quản lý quyền truy cập vào lịch Google Workspace cho tổ chức của bạn.",
"delegation_credential": "Ủy quyền toàn miền",
"delegation_credential_description": "Ủy quyền toàn miền cho phép bạn quản lý quyền truy cập vào lịch Google Workspace cho tổ chức của bạn.",
"add_client_id_in_google_workspace_with_below_scope": "Thêm Client Id này vào Google Workspace với phạm vi dưới đây",
"domain_wide_delegation_enabled": "Đã bật ủy quyền toàn miền",
"domain_wide_delegation_disabled": "Đã tắt ủy quyền toàn miền",
"add_domain_wide_delegation": "Thêm ủy quyền toàn miền",
"edit_domain_wide_delegation": "Chỉnh sửa ủy quyền toàn miền",
"delegation_credential_enabled": "Đã bật ủy quyền toàn miền",
"delegation_credential_disabled": "Đã tắt ủy quyền toàn miền",
"add_delegation_credential": "Thêm ủy quyền toàn miền",
"edit_delegation_credential": "Chỉnh sửa ủy quyền toàn miền",
"domain": "Tên miền",
"no_workspace_platforms": "Không có nền tảng workspace",
"workspace_platform": "Nền tảng workspace",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "JSON khóa tài khoản dịch vụ",
"edit_service_account_key": "Chỉnh sửa JSON khóa tài khoản dịch vụ",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "Ủy quyền toàn miền giới hạn việc thêm nhiều hơn một cài đặt",
"delegation_credential_restricts_adding_more_than_one_installation": "Ủy quyền toàn miền giới hạn việc thêm nhiều hơn một cài đặt",
"app_successfully_installed_and_is_using_delegated_credentials": "Ứng dụng đã được cài đặt thành công và đang sử dụng thông tin xác thực được ủy quyền",
"remove_users_from_org_confirm": "Bạn có chắc chắn muốn xoá bỏ {{userCount}} người dùng khỏi tổ chức này?",
"remove_users_from_team": "Xóa người dùng khỏi nhóm",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "Đã xảy ra lỗi không mong muốn. Vui lòng thử lại sau",
"select_oAuth_client": "Chọn OAuth Client",
"on_every_instance": "Trên mỗi phiên bản",
"domain_wide_delegation_feature_not_enabled": "Tính năng ủy quyền toàn miền chưa được kích hoạt cho tổ chức của bạn",
"delegation_credential_feature_not_enabled": "Tính năng ủy quyền toàn miền chưa được kích hoạt cho tổ chức của bạn",
"internal_booking_note": "Ghi chú nội bộ",
"internal_note_cancellation_reason": "Lý do chia sẻ với khách hàng (tùy chọn)",
"internal_booking_note_description": "Ghi chú này chỉ hiển thị cho các thành viên trong nhóm của bạn",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "此会议是关于什么的?",
"add_to_team": "添加到团队",
"remove_users_from_org": "从组织中移除用户",
"domain_wide_delegation": "域级委托",
"domain_wide_delegation_description": "域级委托允许您管理组织的 Google Workspace 日历访问权限。",
"delegation_credential": "域级委托",
"delegation_credential_description": "域级委托允许您管理组织的 Google Workspace 日历访问权限。",
"add_client_id_in_google_workspace_with_below_scope": "使用以下范围在 Google Workspace 中添加此客户端 ID",
"domain_wide_delegation_enabled": "已启用域级委托",
"domain_wide_delegation_disabled": "已禁用域级委托",
"add_domain_wide_delegation": "添加域级委托",
"edit_domain_wide_delegation": "编辑域级委托",
"delegation_credential_enabled": "已启用域级委托",
"delegation_credential_disabled": "已禁用域级委托",
"add_delegation_credential": "添加域级委托",
"edit_delegation_credential": "编辑域级委托",
"domain": "域名",
"no_workspace_platforms": "无工作区平台",
"workspace_platform": "工作区平台",
@@ -2287,7 +2287,7 @@
"slug": "标识符",
"service_account_key": "服务账号密钥 JSON",
"edit_service_account_key": "编辑服务账号密钥 JSON",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "域级委托限制添加多个安装",
"delegation_credential_restricts_adding_more_than_one_installation": "域级委托限制添加多个安装",
"app_successfully_installed_and_is_using_delegated_credentials": "应用程序已成功安装并正在使用委托凭据",
"remove_users_from_org_confirm": "您确定要从该组织中移除 {{userCount}} 个用户吗?",
"remove_users_from_team": "从团队中移除用户",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "发生意外错误。请稍后再试。",
"select_oAuth_client": "选择 OAuth 客户端",
"on_every_instance": "在每个实例上",
"domain_wide_delegation_feature_not_enabled": "您的组织尚未启用域级委托功能",
"delegation_credential_feature_not_enabled": "您的组织尚未启用域级委托功能",
"internal_booking_note": "内部备注",
"internal_note_cancellation_reason": "与客户共享的原因(可选)",
"internal_booking_note_description": "此备注仅对团队成员可见",
@@ -2269,13 +2269,13 @@
"what_is_this_meeting_about": "此會議的主題是什麼?",
"add_to_team": "新增至團隊",
"remove_users_from_org": "從組織中移除使用者",
"domain_wide_delegation": "網域範圍委派",
"domain_wide_delegation_description": "網域範圍委派允許您管理組織的 Google Workspace 日曆存取權限。",
"delegation_credential": "網域範圍委派",
"delegation_credential_description": "網域範圍委派允許您管理組織的 Google Workspace 日曆存取權限。",
"add_client_id_in_google_workspace_with_below_scope": "請在 Google Workspace 中使用以下範圍新增此客戶端 ID",
"domain_wide_delegation_enabled": "已啟用網域範圍委派",
"domain_wide_delegation_disabled": "已停用網域範圍委派",
"add_domain_wide_delegation": "新增網域範圍委派",
"edit_domain_wide_delegation": "編輯網域範圍委派",
"delegation_credential_enabled": "已啟用網域範圍委派",
"delegation_credential_disabled": "已停用網域範圍委派",
"add_delegation_credential": "新增網域範圍委派",
"edit_delegation_credential": "編輯網域範圍委派",
"domain": "網域",
"no_workspace_platforms": "無工作區平台",
"workspace_platform": "工作區平台",
@@ -2287,7 +2287,7 @@
"slug": "Slug",
"service_account_key": "服務帳戶金鑰 JSON",
"edit_service_account_key": "編輯服務帳戶金鑰 JSON",
"domain_wide_delegation_restricts_adding_more_than_one_installation": "網域範圍委派限制新增多個安裝",
"delegation_credential_restricts_adding_more_than_one_installation": "網域範圍委派限制新增多個安裝",
"app_successfully_installed_and_is_using_delegated_credentials": "應用程式已成功安裝並使用委派憑證",
"remove_users_from_org_confirm": "確定要從此組織移除 {{userCount}} 使用者嗎?",
"remove_users_from_team": "從團隊中移除用戶",
@@ -2953,7 +2953,7 @@
"k_bar_ai_error": "發生意外錯誤。請稍後再試。",
"select_oAuth_client": "選擇 OAuth 用戶端",
"on_every_instance": "在每個執行個體上",
"domain_wide_delegation_feature_not_enabled": "您的組織尚未啟用網域範圍委派功能",
"delegation_credential_feature_not_enabled": "您的組織尚未啟用網域範圍委派功能",
"internal_booking_note": "內部備註",
"internal_note_cancellation_reason": "與客戶分享的原因(選填)",
"internal_booking_note_description": "此備註僅團隊成員可見",
@@ -4,7 +4,7 @@ import {
Timezones,
TestData,
mockCalendar,
createDwdCredential,
createDelegationCredential,
createOrganization,
getOrganizer,
} from "../../utils/bookingScenario/bookingScenario";
@@ -20,8 +20,8 @@ import { setupAndTeardown } from "./setupAndTeardown";
describe("getSchedule", () => {
setupAndTeardown();
describe("Domain Wide Delegation", () => {
test("correctly identifies unavailable slots using DWD credentials", async () => {
describe("Delegation Credential", () => {
test("correctly identifies unavailable slots using DelegationCredential credentials", async () => {
const { dateString: plus1DateString } = getDate({ dateIncrement: 1 });
const { dateString: plus2DateString } = getDate({ dateIncrement: 2 });
@@ -50,13 +50,13 @@ describe("getSchedule", () => {
id: 101,
schedules: [TestData.schedules.IstWorkHours],
selectedCalendars: [TestData.selectedCalendars.google],
// User must be part of organization to be able to use that organization's DWD credential
// User must be part of organization to be able to use that organization's Delegation credential
teams: payloadToMakePartOfOrganization,
// No regular credentials provided
credentials: [],
});
await createDwdCredential(org.id);
await createDelegationCredential(org.id);
const googleCalendarMock = mockCalendar("googlecalendar", {
create: {
@@ -108,7 +108,7 @@ describe("getSchedule", () => {
});
});
test("fails to get schedule when user isn't part of the organization with DWD credential", async () => {
test("fails to get schedule when user isn't part of the organization with Delegation credential", async () => {
const { dateString: plus1DateString } = getDate({ dateIncrement: 1 });
const { dateString: plus2DateString } = getDate({ dateIncrement: 2 });
@@ -147,8 +147,8 @@ describe("getSchedule", () => {
credentials: [],
});
// Create DWD credential for the org user isn't part of
await createDwdCredential(org.id);
// Create Delegation credential for the org user isn't part of
await createDelegationCredential(org.id);
const googleCalendarMock = mockCalendar("googlecalendar", {
create: {
@@ -196,7 +196,7 @@ describe("getSchedule", () => {
expectNoAttemptToGetAvailability(googleCalendarMock);
// All slots would be available as no DWD credentials are available
// All slots would be available as no DelegationCredential credentials are available
expect(scheduleForDayWithAGoogleCalendarBooking).toHaveTimeSlots(
expectedSlotsForSchedule.IstWorkHours.interval["1hr"].allPossibleSlotsStartingAt430,
{
@@ -2309,7 +2309,7 @@ export const getDefaultBookingFields = ({
] as Fields;
};
export const createDwdCredential = async (orgId: number, type: "google" | "office365" = "google") => {
export const createDelegationCredential = async (orgId: number, type: "google" | "office365" = "google") => {
if (type === "google") {
const encryptedServiceAccountKey = {
type: "service_account",
@@ -2349,7 +2349,7 @@ export const createDwdCredential = async (orgId: number, type: "google" | "offic
auth_provider_x509_cert_url: "AUTH_PROVIDER_X509_CERT_URL",
};
const dwd = await prismock.domainWideDelegation.create({
const delegationCredential = await prismock.delegationCredential.create({
data: {
workspacePlatform: {
connect: {
@@ -2368,7 +2368,7 @@ export const createDwdCredential = async (orgId: number, type: "google" | "offic
},
});
return { ...dwd, serviceAccountKey: decryptedServiceAccountKey };
return { ...delegationCredential, serviceAccountKey: decryptedServiceAccountKey };
} else if (type === "office365") {
const encryptedServiceAccountKey = {
client_id: "CLIENT_ID",
@@ -2392,7 +2392,7 @@ export const createDwdCredential = async (orgId: number, type: "google" | "offic
tenant_id: "TENANT_ID",
};
const dwd = await prismock.domainWideDelegation.create({
const delegationCredential = await prismock.delegationCredential.create({
data: {
workspacePlatform: {
connect: {
@@ -2411,13 +2411,13 @@ export const createDwdCredential = async (orgId: number, type: "google" | "offic
},
});
return { ...dwd, serviceAccountKey: decryptedServiceAccountKey };
return { ...delegationCredential, serviceAccountKey: decryptedServiceAccountKey };
}
throw new Error(`Unsupported type: ${type}`);
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const buildDwdCredential = ({ serviceAccountKey }: { serviceAccountKey: any }) => {
export const buildDelegationCredential = ({ serviceAccountKey }: { serviceAccountKey: any }) => {
return {
id: -1,
key: {
File diff suppressed because it is too large Load Diff
+8 -6
View File
@@ -1,7 +1,7 @@
import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
import { getAppFromSlug } from "@calcom/app-store/utils";
import getInstallCountPerApp from "@calcom/lib/apps/getInstallCountPerApp";
import { getAllDwdCredentialsForUser } from "@calcom/lib/domainWideDelegation/server";
import { getAllDelegationCredentialsForUser } from "@calcom/lib/delegationCredential/server";
import type { UserAdminTeams } from "@calcom/lib/server/repository/user";
import prisma, { safeAppSelect, safeCredentialSelect } from "@calcom/prisma";
import { userMetadata } from "@calcom/prisma/zod-utils";
@@ -92,8 +92,8 @@ export async function getAppRegistryWithCredentials(userId: number, userAdminTea
},
});
const dwdCredentials = user
? await getAllDwdCredentialsForUser({ user: { id: userId, email: user.email } })
const delegationCredentials = user
? await getAllDelegationCredentialsForUser({ user: { id: userId, email: user.email } })
: [];
const usersDefaultApp = userMetadata.parse(user?.metadata)?.defaultConferencingApp?.appSlug;
@@ -103,9 +103,11 @@ export async function getAppRegistryWithCredentials(userId: number, userAdminTea
})[];
const installCountPerApp = await getInstallCountPerApp();
for await (const dbapp of dbApps) {
const dwdCredentialsForApp = dwdCredentials.filter((credential) => credential.appId === dbapp.slug);
const nonDwdCredentialsForApp = dbapp.credentials;
const allCredentials = [...dwdCredentialsForApp, ...nonDwdCredentialsForApp];
const delegationCredentialsForApp = delegationCredentials.filter(
(credential) => credential.appId === dbapp.slug
);
const nonDelegationCredentialsForApp = dbapp.credentials;
const allCredentials = [...delegationCredentialsForApp, ...nonDelegationCredentialsForApp];
const app = await getAppWithMetadata(dbapp);
if (!app) continue;
// Skip if app isn't installed
@@ -19,7 +19,7 @@ export const metadata = {
email: "help@cal.com",
dirName: "googlecalendar",
isOAuth: true,
domainWideDelegation: {
delegationCredential: {
// This is unused at the moment but should be used in future
// For now, we have hardcoded imports in the codebase that are supported with Google Workspace(i.e. Google Calendar and Google Meet)
workspacePlatformSlug: "google",
@@ -1,6 +1,6 @@
import type { NextApiRequest } from "next";
import { buildNonDwdCredential } from "@calcom/lib/domainWideDelegation/server";
import { buildNonDelegationCredential } from "@calcom/lib/delegationCredential/server";
import { HttpError } from "@calcom/lib/http-error";
import { defaultHandler } from "@calcom/lib/server/defaultHandler";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
@@ -38,7 +38,7 @@ async function postHandler(req: NextApiRequest) {
});
const { selectedCalendars } = credential;
const calendar = await getCalendar(buildNonDwdCredential(credential));
const calendar = await getCalendar(buildNonDelegationCredential(credential));
// Make sure to pass unique SelectedCalendars to avoid unnecessary third party api calls
// Necessary to do here so that it is ensure for all calendar apps
@@ -295,7 +295,7 @@ describe("Watching and unwatching calendar", () => {
integration: "google_calendar",
externalId: "example@cal.com",
credentialId: 1,
domainWideDelegationCredentialId: null,
delegationCredentialId: null,
googleChannelId: "mock-channel-id",
googleChannelKind: "api#channel",
googleChannelResourceId: "mock-resource-id",
@@ -325,7 +325,7 @@ describe("Watching and unwatching calendar", () => {
integration: "google_calendar",
externalId: "example@cal.com",
credentialId: 1,
domainWideDelegationCredentialId: null,
delegationCredentialId: null,
googleChannelId: null,
googleChannelKind: null,
googleChannelResourceId: null,
@@ -711,13 +711,13 @@ describe("GoogleCalendarService credential handling", () => {
return mockJWTInstance;
};
test("uses JWT auth with impersonation when DWD credential is provided", async () => {
const credentialWithDWD = await createCredentialInDb({
test("uses JWT auth with impersonation when Delegation credential is provided", async () => {
const credentialWithDelegation = await createCredentialInDb({
user: { email: "user@example.com" },
delegatedTo: delegatedCredential,
});
const calendarService = new CalendarService(credentialWithDWD);
const calendarService = new CalendarService(credentialWithDelegation);
await calendarService.listCalendars();
const expectedJWTConfig: MockJWT = {
@@ -738,7 +738,7 @@ describe("GoogleCalendarService credential handling", () => {
});
});
test("uses OAuth2 auth when no DWD credential is provided", async () => {
test("uses OAuth2 auth when no Delegation credential is provided", async () => {
const regularCredential = await createCredentialInDb();
const { client_id, client_secret, redirect_uris } = await getGoogleAppKeys();
@@ -762,8 +762,8 @@ describe("GoogleCalendarService credential handling", () => {
});
});
test("handles DWD authorization errors appropriately", async () => {
const credentialWithDWD = await createCredentialInDb({
test("handles DelegationCredential authorization errors appropriately", async () => {
const credentialWithDelegation = await createCredentialInDb({
user: { email: "user@example.com" },
delegatedTo: delegatedCredential,
});
@@ -778,15 +778,15 @@ describe("GoogleCalendarService credential handling", () => {
},
});
const calendarService = new CalendarService(credentialWithDWD);
const calendarService = new CalendarService(credentialWithDelegation);
await expect(calendarService.listCalendars()).rejects.toThrow(
"Make sure that the Client ID for the domain wide delegation is added to the Google Workspace Admin Console"
"Make sure that the Client ID for the delegation credential is added to the Google Workspace Admin Console"
);
});
test("handles invalid_grant error (user not in workspace) appropriately", async () => {
const credentialWithDWD = await createCredentialInDb({
const credentialWithDelegation = await createCredentialInDb({
user: { email: "user@example.com" },
delegatedTo: delegatedCredential,
});
@@ -801,13 +801,13 @@ describe("GoogleCalendarService credential handling", () => {
},
});
const calendarService = new CalendarService(credentialWithDWD);
const calendarService = new CalendarService(credentialWithDelegation);
await expect(calendarService.listCalendars()).rejects.toThrow("User might not exist in Google Workspace");
});
test("handles general DWD authorization errors appropriately", async () => {
const credentialWithDWD = await createCredentialInDb({
test("handles general DelegationCredential authorization errors appropriately", async () => {
const credentialWithDelegation = await createCredentialInDb({
user: { email: "user@example.com" },
delegatedTo: delegatedCredential,
});
@@ -816,18 +816,18 @@ describe("GoogleCalendarService credential handling", () => {
authorizeError: new Error("Some unexpected error"),
});
const calendarService = new CalendarService(credentialWithDWD);
const calendarService = new CalendarService(credentialWithDelegation);
await expect(calendarService.listCalendars()).rejects.toThrow("Error authorizing domain wide delegation");
await expect(calendarService.listCalendars()).rejects.toThrow("Error authorizing delegation credential");
});
test("handles missing user email for DWD appropriately", async () => {
const credentialWithDWD = await createCredentialInDb({
test("handles missing user email for DelegationCredential appropriately", async () => {
const credentialWithDelegation = await createCredentialInDb({
user: { email: null },
delegatedTo: delegatedCredential,
});
const calendarService = new CalendarService(credentialWithDWD);
const calendarService = new CalendarService(credentialWithDelegation);
const { client_id, client_secret, redirect_uris } = await getGoogleAppKeys();
await calendarService.listCalendars();
@@ -13,9 +13,9 @@ import type { FreeBusyArgs } from "@calcom/features/calendar-cache/calendar-cach
import { getTimeMax, getTimeMin } from "@calcom/features/calendar-cache/lib/datesForCache";
import { getLocation, getRichDescription } from "@calcom/lib/CalEventParser";
import {
CalendarAppDomainWideDelegationClientIdNotAuthorizedError,
CalendarAppDomainWideDelegationInvalidGrantError,
CalendarAppDomainWideDelegationError,
CalendarAppDelegationCredentialClientIdNotAuthorizedError,
CalendarAppDelegationCredentialInvalidGrantError,
CalendarAppDelegationCredentialError,
} from "@calcom/lib/CalendarAppError";
import { uniqueBy } from "@calcom/lib/array";
import {
@@ -180,12 +180,12 @@ export default class GoogleCalendarService implements Calendar {
};
};
private getAuthedCalendarFromDwd = async ({
domainWideDelegation,
private getAuthedCalendarFromDelegationCredential = async ({
delegationCredential,
emailToImpersonate,
}: {
emailToImpersonate: string;
domainWideDelegation: {
delegationCredential: {
serviceAccountKey: {
client_email: string;
client_id: string;
@@ -193,9 +193,9 @@ export default class GoogleCalendarService implements Calendar {
};
};
}) => {
const serviceAccountClientEmail = domainWideDelegation.serviceAccountKey.client_email;
const serviceAccountClientId = domainWideDelegation.serviceAccountKey.client_id;
const serviceAccountPrivateKey = domainWideDelegation.serviceAccountKey.private_key;
const serviceAccountClientEmail = delegationCredential.serviceAccountKey.client_email;
const serviceAccountClientId = delegationCredential.serviceAccountKey.client_id;
const serviceAccountPrivateKey = delegationCredential.serviceAccountKey.private_key;
const authClient = new JWT({
email: serviceAccountClientEmail,
@@ -207,26 +207,26 @@ export default class GoogleCalendarService implements Calendar {
try {
await authClient.authorize();
} catch (error) {
this.log.error("DWD: Error authorizing domain wide delegation", JSON.stringify(error));
this.log.error("DelegationCredential: Error authorizing delegation credential", JSON.stringify(error));
if ((error as any).response?.data?.error === "unauthorized_client") {
throw new CalendarAppDomainWideDelegationClientIdNotAuthorizedError(
"Make sure that the Client ID for the domain wide delegation is added to the Google Workspace Admin Console"
throw new CalendarAppDelegationCredentialClientIdNotAuthorizedError(
"Make sure that the Client ID for the delegation credential is added to the Google Workspace Admin Console"
);
}
if ((error as any).response?.data?.error === "invalid_grant") {
throw new CalendarAppDomainWideDelegationInvalidGrantError(
throw new CalendarAppDelegationCredentialInvalidGrantError(
"User might not exist in Google Workspace"
);
}
// Catch all error
throw new CalendarAppDomainWideDelegationError("Error authorizing domain wide delegation");
throw new CalendarAppDelegationCredentialError("Error authorizing delegation credential");
}
this.log.debug(
"Using domain wide delegation with service account email",
"Using delegation credential with service account email",
safeStringify({
serviceAccountClientEmail,
serviceAccountClientId,
@@ -240,22 +240,22 @@ export default class GoogleCalendarService implements Calendar {
};
public authedCalendar = async () => {
let dwdAuthedCalendar;
let delegationCredentialAuthedCalendar;
if (this.credential.delegatedTo) {
if (!this.credential.user?.email) {
this.log.error("DWD: No email to impersonate found for domain wide delegation");
this.log.error("DelegationCredential: No email to impersonate found for delegation credential");
} else {
const oauthClientIdAliasRegex = /\+[a-zA-Z0-9]{25}/;
dwdAuthedCalendar = await this.getAuthedCalendarFromDwd({
domainWideDelegation: this.credential.delegatedTo,
delegationCredentialAuthedCalendar = await this.getAuthedCalendarFromDelegationCredential({
delegationCredential: this.credential.delegatedTo,
emailToImpersonate: this.credential.user.email.replace(oauthClientIdAliasRegex, ""),
});
}
}
if (dwdAuthedCalendar) {
return dwdAuthedCalendar;
if (delegationCredentialAuthedCalendar) {
return delegationCredentialAuthedCalendar;
}
const myGoogleAuth = await this.auth.getMyGoogleAuthWithRefreshedToken();
@@ -865,8 +865,8 @@ export default class GoogleCalendarService implements Calendar {
}
}
// It would error if the domain wide delegation is not set up correctly
async testDomainWideDelegationSetup() {
// It would error if the delegation credential is not set up correctly
async testDelegationCredentialSetup() {
const calendar = await this.authedCalendar();
const cals = await calendar.calendarList.list({ fields: "items(id)" });
return !!cals.data.items;
@@ -4,8 +4,8 @@ import type { DefaultBodyType } from "msw";
import dayjs from "@calcom/dayjs";
import { getLocation, getRichDescription } from "@calcom/lib/CalEventParser";
import {
CalendarAppDomainWideDelegationInvalidGrantError,
CalendarAppDomainWideDelegationConfigurationError,
CalendarAppDelegationCredentialInvalidGrantError,
CalendarAppDelegationCredentialConfigurationError,
} from "@calcom/lib/CalendarAppError";
import { handleErrorsJson, handleErrorsRaw } from "@calcom/lib/errors";
import logger from "@calcom/lib/logger";
@@ -72,27 +72,24 @@ export default class Office365CalendarService implements Calendar {
appSlug: metadata.slug,
currentTokenObject: tokenResponse,
fetchNewTokenObject: async ({ refreshToken }: { refreshToken: string | null }) => {
const isDomainWideDelegated = Boolean(credential?.delegatedTo);
const isDelegated = Boolean(credential?.delegatedTo);
if (!isDomainWideDelegated && !refreshToken) {
if (!isDelegated && !refreshToken) {
return null;
}
const { client_id, client_secret } = await this.getAuthCredentials(isDomainWideDelegated);
const { client_id, client_secret } = await this.getAuthCredentials(isDelegated);
const url = this.getAuthUrl(
isDomainWideDelegated,
credential?.delegatedTo?.serviceAccountKey?.tenant_id
);
const url = this.getAuthUrl(isDelegated, credential?.delegatedTo?.serviceAccountKey?.tenant_id);
const bodyParams = {
scope: isDomainWideDelegated
scope: isDelegated
? "https://graph.microsoft.com/.default"
: "User.Read Calendars.Read Calendars.ReadWrite",
client_id,
client_secret,
grant_type: isDomainWideDelegated ? "client_credentials" : "refresh_token",
...(isDomainWideDelegated ? {} : { refresh_token: refreshToken ?? "" }),
grant_type: isDelegated ? "client_credentials" : "refresh_token",
...(isDelegated ? {} : { refresh_token: refreshToken ?? "" }),
};
return fetch(url, {
@@ -127,8 +124,8 @@ export default class Office365CalendarService implements Calendar {
private getAuthUrl(delegatedTo: boolean, tenantId?: string): string {
if (delegatedTo) {
if (!tenantId) {
throw new CalendarAppDomainWideDelegationInvalidGrantError(
"Invalid DomainWideDelegation Settings: tenantId is missing"
throw new CalendarAppDelegationCredentialInvalidGrantError(
"Invalid DelegationCredential Settings: tenantId is missing"
);
}
return `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
@@ -137,14 +134,14 @@ export default class Office365CalendarService implements Calendar {
return "https://login.microsoftonline.com/common/oauth2/v2.0/token";
}
private async getAuthCredentials(isDomainWideDelegated: boolean) {
if (isDomainWideDelegated) {
private async getAuthCredentials(isDelegated: boolean) {
if (isDelegated) {
const client_id = this.credential?.delegatedTo?.serviceAccountKey?.client_id;
const client_secret = this.credential?.delegatedTo?.serviceAccountKey?.private_key;
if (!client_id || !client_secret) {
throw new CalendarAppDomainWideDelegationConfigurationError(
"Domain Wide Delegated credential without clientId or Secret"
throw new CalendarAppDelegationCredentialConfigurationError(
"Delegation credential without clientId or Secret"
);
}
@@ -157,18 +154,18 @@ export default class Office365CalendarService implements Calendar {
private async getAzureUserId(credential: CredentialForCalendarServiceWithTenantId) {
if (this.azureUserId) return this.azureUserId;
const isDomainWideDelegated = Boolean(credential?.delegatedTo);
const isDelegated = Boolean(credential?.delegatedTo);
if (!isDomainWideDelegated) return;
if (!isDelegated) return;
const url = this.getAuthUrl(isDomainWideDelegated, credential?.delegatedTo?.serviceAccountKey?.tenant_id);
const url = this.getAuthUrl(isDelegated, credential?.delegatedTo?.serviceAccountKey?.tenant_id);
const dwdClientId = credential.delegatedTo?.serviceAccountKey?.client_id;
const dwdClientSecret = credential.delegatedTo?.serviceAccountKey?.private_key;
const delegationCredentialClientId = credential.delegatedTo?.serviceAccountKey?.client_id;
const delegationCredentialClientSecret = credential.delegatedTo?.serviceAccountKey?.private_key;
if (!dwdClientId || !dwdClientSecret) {
throw new CalendarAppDomainWideDelegationConfigurationError(
"Domain Wide Delegated credential without clientId or Secret"
if (!delegationCredentialClientId || !delegationCredentialClientSecret) {
throw new CalendarAppDelegationCredentialConfigurationError(
"Delegation credential without clientId or Secret"
);
}
const loginResponse = await fetch(url, {
@@ -176,9 +173,9 @@ export default class Office365CalendarService implements Calendar {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
scope: "https://graph.microsoft.com/.default",
client_id: dwdClientId,
client_id: delegationCredentialClientId,
grant_type: "client_credentials",
client_secret: dwdClientSecret,
client_secret: delegationCredentialClientSecret,
}),
});
@@ -202,7 +199,7 @@ export default class Office365CalendarService implements Calendar {
const parsedBody = await response.json();
if (!parsedBody?.value?.[0]?.id) {
throw new CalendarAppDomainWideDelegationInvalidGrantError(
throw new CalendarAppDelegationCredentialInvalidGrantError(
"User might not exist in Microsoft Azure Active Directory"
);
}
@@ -211,16 +208,16 @@ export default class Office365CalendarService implements Calendar {
return this.azureUserId;
}
// It would error if the domain wide delegation is not set up correctly
async testDomainWideDelegationSetup(): Promise<boolean> {
const dwdClientId = this.credential.delegatedTo?.serviceAccountKey?.client_id;
const dwdClientSecret = this.credential.delegatedTo?.serviceAccountKey?.private_key;
// It would error if the delegation credential is not set up correctly
async testDelegationCredentialSetup(): Promise<boolean> {
const delegationCredentialClientId = this.credential.delegatedTo?.serviceAccountKey?.client_id;
const delegationCredentialClientSecret = this.credential.delegatedTo?.serviceAccountKey?.private_key;
const url = this.getAuthUrl(
Boolean(this.credential?.delegatedTo),
this.credential?.delegatedTo?.serviceAccountKey?.tenant_id
);
if (!dwdClientId || !dwdClientSecret) {
if (!delegationCredentialClientId || !delegationCredentialClientSecret) {
return false;
}
const loginResponse = await fetch(url, {
@@ -228,9 +225,9 @@ export default class Office365CalendarService implements Calendar {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
scope: "https://graph.microsoft.com/.default",
client_id: dwdClientId,
client_id: delegationCredentialClientId,
grant_type: "client_credentials",
client_secret: dwdClientSecret,
client_secret: delegationCredentialClientSecret,
}),
});
const parsedLoginResponse = await loginResponse.json();
@@ -1,8 +1,8 @@
import { z } from "zod";
import {
CalendarAppDomainWideDelegationConfigurationError,
CalendarAppDomainWideDelegationInvalidGrantError,
CalendarAppDelegationCredentialConfigurationError,
CalendarAppDelegationCredentialInvalidGrantError,
} from "@calcom/lib/CalendarAppError";
import { handleErrorsRaw } from "@calcom/lib/errors";
import { HttpError } from "@calcom/lib/http-error";
@@ -49,26 +49,26 @@ const TeamsVideoApiAdapter = (credential: CredentialForCalendarServiceWithTenant
appSlug: config.slug,
currentTokenObject: tokenResponse,
fetchNewTokenObject: async ({ refreshToken }: { refreshToken: string | null }) => {
const isDomainWideDelegated = Boolean(credential?.delegatedTo);
if (!isDomainWideDelegated && !refreshToken) {
const isDelegated = Boolean(credential?.delegatedTo);
if (!isDelegated && !refreshToken) {
return null;
}
const credentials = isDomainWideDelegated
const credentials = isDelegated
? {
client_id: credential?.delegatedTo?.serviceAccountKey?.client_id,
client_secret: credential?.delegatedTo?.serviceAccountKey?.private_key,
}
: await getO365VideoAppKeys();
if (isDomainWideDelegated && (!credentials.client_id || !credentials.client_secret)) {
throw new CalendarAppDomainWideDelegationConfigurationError(
"Domain Wide Delegated credential without clientId or Secret"
if (isDelegated && (!credentials.client_id || !credentials.client_secret)) {
throw new CalendarAppDelegationCredentialConfigurationError(
"Delegation credential without clientId or Secret"
);
}
const url = getAuthUrl(isDomainWideDelegated, credential?.delegatedTo?.serviceAccountKey?.tenant_id);
const scope = isDomainWideDelegated
const url = getAuthUrl(isDelegated, credential?.delegatedTo?.serviceAccountKey?.tenant_id);
const scope = isDelegated
? "https://graph.microsoft.com/.default"
: "User.Read Calendars.Read Calendars.ReadWrite";
@@ -76,8 +76,8 @@ const TeamsVideoApiAdapter = (credential: CredentialForCalendarServiceWithTenant
scope,
client_id: credentials.client_id || "",
client_secret: credentials.client_secret || "",
grant_type: isDomainWideDelegated ? "client_credentials" : "refresh_token",
...(isDomainWideDelegated ? {} : { refresh_token: refreshToken ?? "" }),
grant_type: isDelegated ? "client_credentials" : "refresh_token",
...(isDelegated ? {} : { refresh_token: refreshToken ?? "" }),
};
return await fetch(url, {
@@ -108,8 +108,8 @@ const TeamsVideoApiAdapter = (credential: CredentialForCalendarServiceWithTenant
function getAuthUrl(delegatedTo: boolean, tenantId?: string): string {
if (delegatedTo) {
if (!tenantId) {
throw new CalendarAppDomainWideDelegationInvalidGrantError(
"Invalid DomainWideDelegation Settings: tenantId is missing"
throw new CalendarAppDelegationCredentialInvalidGrantError(
"Invalid DelegationCredential Settings: tenantId is missing"
);
}
return `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
@@ -129,18 +129,18 @@ const TeamsVideoApiAdapter = (credential: CredentialForCalendarServiceWithTenant
async function getAzureUserId(credential: CredentialForCalendarServiceWithTenantId) {
if (azureUserId) return azureUserId;
const isDomainWideDelegated = Boolean(credential?.delegatedTo);
const isDelegated = Boolean(credential?.delegatedTo);
if (!isDomainWideDelegated) return null;
if (!isDelegated) return null;
const url = getAuthUrl(isDomainWideDelegated, credential?.delegatedTo?.serviceAccountKey?.tenant_id);
const url = getAuthUrl(isDelegated, credential?.delegatedTo?.serviceAccountKey?.tenant_id);
const dwdClientId = credential.delegatedTo?.serviceAccountKey?.client_id;
const dwdClientSecret = credential.delegatedTo?.serviceAccountKey?.private_key;
const delegationCredentialClientId = credential.delegatedTo?.serviceAccountKey?.client_id;
const delegationCredentialClientSecret = credential.delegatedTo?.serviceAccountKey?.private_key;
if (!dwdClientId || !dwdClientSecret) {
throw new CalendarAppDomainWideDelegationConfigurationError(
"Domain Wide Delegated credential without clientId or Secret"
if (!delegationCredentialClientId || !delegationCredentialClientSecret) {
throw new CalendarAppDelegationCredentialConfigurationError(
"Delegation credential without clientId or Secret"
);
}
const loginResponse = await fetch(url, {
@@ -148,9 +148,9 @@ const TeamsVideoApiAdapter = (credential: CredentialForCalendarServiceWithTenant
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
scope: "https://graph.microsoft.com/.default",
client_id: dwdClientId,
client_id: delegationCredentialClientId,
grant_type: "client_credentials",
client_secret: dwdClientSecret,
client_secret: delegationCredentialClientSecret,
}),
});
@@ -173,7 +173,7 @@ const TeamsVideoApiAdapter = (credential: CredentialForCalendarServiceWithTenant
const parsedBody = await response.json();
if (!parsedBody?.value?.[0]?.id) {
throw new CalendarAppDomainWideDelegationInvalidGrantError(
throw new CalendarAppDelegationCredentialInvalidGrantError(
"User might not exist in Microsoft Azure Active Directory"
);
}
+15 -13
View File
@@ -4,9 +4,9 @@ import type { TFunction } from "next-i18next";
import { defaultVideoAppCategories } from "@calcom/app-store/utils";
import getEnabledAppsFromCredentials from "@calcom/lib/apps/getEnabledAppsFromCredentials";
import {
buildNonDwdCredentials,
enrichUserWithDwdConferencingCredentialsWithoutOrgId,
} from "@calcom/lib/domainWideDelegation/server";
buildNonDelegationCredentials,
enrichUserWithDelegationConferencingCredentialsWithoutOrgId,
} from "@calcom/lib/delegationCredential/server";
import { prisma } from "@calcom/prisma";
import { AppCategories } from "@calcom/prisma/enums";
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
@@ -63,7 +63,7 @@ export async function getLocationGroupedOptions(
});
}
const nonDwdCredentials = await prisma.credential.findMany({
const nonDelegationCredentials = await prisma.credential.findMany({
where: {
...idToSearchObject,
app: {
@@ -84,17 +84,19 @@ export async function getLocationGroupedOptions(
let credentials;
if (user) {
// We only add dwd credentials if the request for location options is for a user because DWD Credential is applicable to Users only.
const { credentials: allCredentials } = await enrichUserWithDwdConferencingCredentialsWithoutOrgId({
user: {
...user,
credentials: nonDwdCredentials,
},
});
// We only add delegationCredentials if the request for location options is for a user because DelegationCredential Credential is applicable to Users only.
const { credentials: allCredentials } = await enrichUserWithDelegationConferencingCredentialsWithoutOrgId(
{
user: {
...user,
credentials: nonDelegationCredentials,
},
}
);
credentials = allCredentials;
} else {
// TODO: We can avoid calling buildNonDwdCredentials here by moving the above prisma query to the repository and doing it there
credentials = buildNonDwdCredentials(nonDwdCredentials);
// TODO: We can avoid calling buildNonDelegationCredentials here by moving the above prisma query to the repository and doing it there
credentials = buildNonDelegationCredentials(nonDelegationCredentials);
}
const integrations = await getEnabledAppsFromCredentials(credentials, { filterOnCredentials: true });
@@ -12,7 +12,7 @@ import type {
EventTypes,
} from "@calcom/features/eventtypes/components/BulkEditDefaultForEventsModal";
import { BulkEditDefaultForEventsModal } from "@calcom/features/eventtypes/components/BulkEditDefaultForEventsModal";
import { isDwdCredential } from "@calcom/lib/domainWideDelegation/clientAndServer";
import { isDelegationCredential } from "@calcom/lib/delegationCredential/clientAndServer";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { AppCategories } from "@calcom/prisma/enums";
import { type RouterOutputs } from "@calcom/trpc";
@@ -230,7 +230,7 @@ function ConnectOrDisconnectIntegrationMenuItem(props: {
<DropdownItem
color="destructive"
onClick={() => handleDisconnect(credentialId, app, teamId)}
disabled={isGlobal || isDwdCredential({ credentialId })}
disabled={isGlobal || isDelegationCredential({ credentialId })}
StartIcon="trash">
{t("remove_app")}
</DropdownItem>
@@ -2,7 +2,7 @@
import { useState } from "react";
import { isDwdCredential } from "@calcom/lib/domainWideDelegation/clientAndServer";
import { isDelegationCredential } from "@calcom/lib/delegationCredential/clientAndServer";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import type { ButtonProps } from "@calcom/ui";
@@ -38,8 +38,8 @@ export default function DisconnectIntegration(props: {
},
});
// Such a credential is added in-memory and removed when Domain-wide delegation is disabled.
const disableDisconnect = isDwdCredential({ credentialId });
// Such a credential is added in-memory and removed when Delegation credential is disabled.
const disableDisconnect = isDelegationCredential({ credentialId });
return (
<DisconnectIntegrationComponent
onDeletionConfirmation={() => mutation.mutate({ id: credentialId, ...(teamId ? { teamId } : {}) })}
@@ -1,6 +1,6 @@
import type z from "zod";
import { enrichUserWithDwdCredentialsWithoutOrgId } from "@calcom/lib/domainWideDelegation/server";
import { enrichUserWithDelegationCredentialsWithoutOrgId } from "@calcom/lib/delegationCredential/server";
import { UserRepository } from "@calcom/lib/server/repository/user";
import prisma from "@calcom/prisma";
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
@@ -120,9 +120,9 @@ export const getAllCredentials = async (
}
});
const userWithDwdCredentials = await enrichUserWithDwdCredentialsWithoutOrgId({
const userWithDelegationCredentials = await enrichUserWithDelegationCredentialsWithoutOrgId({
user: { ...user, credentials: allCredentials },
});
return userWithDwdCredentials.credentials;
return userWithDelegationCredentials.credentials;
};
@@ -1,7 +1,7 @@
import async from "async";
import { isDwdCredential } from "@calcom/lib/domainWideDelegation/clientAndServer";
import { buildAllCredentials } from "@calcom/lib/domainWideDelegation/server";
import { isDelegationCredential } from "@calcom/lib/delegationCredential/clientAndServer";
import { buildAllCredentials } from "@calcom/lib/delegationCredential/server";
import type { CredentialForCalendarService } from "@calcom/types/Credential";
import { refreshCredential } from "./refreshCredential";
@@ -14,8 +14,12 @@ import { refreshCredential } from "./refreshCredential";
export async function refreshCredentials(
credentials: Array<CredentialForCalendarService>
): Promise<Array<CredentialForCalendarService>> {
const nonDwdCredentials = credentials.filter((cred) => !isDwdCredential({ credentialId: cred.id }));
const dwdCredentials = credentials.filter((cred) => isDwdCredential({ credentialId: cred.id }));
const refreshedDbCredentials = await async.mapLimit(nonDwdCredentials, 5, refreshCredential);
return buildAllCredentials({ dwdCredentials, existingCredentials: refreshedDbCredentials });
const nonDelegationCredentials = credentials.filter(
(cred) => !isDelegationCredential({ credentialId: cred.id })
);
const delegationCredentials = credentials.filter((cred) =>
isDelegationCredential({ credentialId: cred.id })
);
const refreshedDbCredentials = await async.mapLimit(nonDelegationCredentials, 5, refreshCredential);
return buildAllCredentials({ delegationCredentials, existingCredentials: refreshedDbCredentials });
}
@@ -70,7 +70,7 @@ async function getBookingToDelete(id: number | undefined, uid: string | undefine
externalCalendarId: true,
credentialId: true,
thirdPartyRecurringEventId: true,
domainWideDelegationCredentialId: true,
delegationCredentialId: true,
},
},
payment: true,
@@ -48,9 +48,9 @@ import EventManager from "@calcom/lib/EventManager";
import { shouldIgnoreContactOwner } from "@calcom/lib/bookings/routing/utils";
import { getDefaultEvent, getUsernameList } from "@calcom/lib/defaultEvents";
import {
enrichHostsWithDwdCredentials,
getFirstDwdConferencingCredentialAppLocation,
} from "@calcom/lib/domainWideDelegation/server";
enrichHostsWithDelegationCredentials,
getFirstDelegationConferencingCredentialAppLocation,
} from "@calcom/lib/delegationCredential/server";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import { getEventName } from "@calcom/lib/event";
@@ -690,7 +690,7 @@ async function handler(
// find a lucky user that is not already in the luckyUsers array
availableUsers: freeUsers,
allRRHosts: (
await enrichHostsWithDwdCredentials({
await enrichHostsWithDelegationCredentials({
orgId: firstUserOrgId ?? null,
hosts: eventTypeWithUsers.hosts,
})
@@ -809,7 +809,7 @@ async function handler(
}
}
const organizationDefaultLocation = getFirstDwdConferencingCredentialAppLocation({
const organizationDefaultLocation = getFirstDelegationConferencingCredentialAppLocation({
credentials: firstUser.credentials,
});
@@ -1,4 +1,4 @@
import { getFirstDwdConferencingCredentialAppLocation } from "@calcom/lib/domainWideDelegation/server";
import { getFirstDelegationConferencingCredentialAppLocation } from "@calcom/lib/delegationCredential/server";
import type { Prisma } from "@calcom/prisma/client";
import { userMetadata as userMetadataSchema } from "@calcom/prisma/zod-utils";
import type { CredentialForCalendarService } from "@calcom/types/Credential";
@@ -36,13 +36,14 @@ export const getLocationValuesForDb = <
users = sortUsersByDynamicList(users, dynamicUserList);
const firstDynamicGroupMember = users[0];
const firstDynamicGroupMemberMetadata = userMetadataSchema.parse(firstDynamicGroupMember.metadata);
const firstDynamicGroupMemberDwdConferencingAppLocation = getFirstDwdConferencingCredentialAppLocation({
credentials: firstDynamicGroupMember.credentials,
});
const firstDynamicGroupMemberDelegationCredentialConferencingAppLocation =
getFirstDelegationConferencingCredentialAppLocation({
credentials: firstDynamicGroupMember.credentials,
});
firstDynamicGroupMemberDefaultLocationUrl =
firstDynamicGroupMemberMetadata?.defaultConferencingApp?.appLink ||
firstDynamicGroupMemberDwdConferencingAppLocation;
firstDynamicGroupMemberDelegationCredentialConferencingAppLocation;
locationBodyString = firstDynamicGroupMemberDefaultLocationUrl || locationBodyString;
}
@@ -2,8 +2,8 @@ import type { Prisma } from "@prisma/client";
import type { IncomingMessage } from "http";
import type { Logger } from "tslog";
import { findQualifiedHostsWithDwdCredentials } from "@calcom/lib/bookings/findQualifiedHostsWithDwdCredentials";
import { enrichUsersWithDwdCredentials } from "@calcom/lib/domainWideDelegation/server";
import { findQualifiedHostsWithDelegationCredentials } from "@calcom/lib/bookings/findQualifiedHostsWithDelegationCredentials";
import { enrichUsersWithDelegationCredentials } from "@calcom/lib/delegationCredential/server";
import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId";
import { HttpError } from "@calcom/lib/http-error";
import { getPiiFreeUser } from "@calcom/lib/piiFreeData";
@@ -25,7 +25,10 @@ type Users = (Awaited<ReturnType<typeof loadUsers>>[number] & {
createdAt?: Date;
})[];
export type UsersWithDwdCredentials = (Omit<Awaited<ReturnType<typeof loadUsers>>[number], "credentials"> & {
export type UsersWithDelegationCredentials = (Omit<
Awaited<ReturnType<typeof loadUsers>>[number],
"credentials"
> & {
isFixed?: boolean;
metadata?: Prisma.JsonValue;
createdAt?: Date;
@@ -72,9 +75,9 @@ export async function loadAndValidateUsers({
rescheduleUid,
routingFormResponse,
}: InputProps): Promise<{
qualifiedRRUsers: UsersWithDwdCredentials;
additionalFallbackRRUsers: UsersWithDwdCredentials;
fixedUsers: UsersWithDwdCredentials;
qualifiedRRUsers: UsersWithDelegationCredentials;
additionalFallbackRRUsers: UsersWithDelegationCredentials;
fixedUsers: UsersWithDelegationCredentials;
}> {
let users: Users = await loadUsers({
eventType,
@@ -125,13 +128,14 @@ export async function loadAndValidateUsers({
? false
: user.isFixed || eventType.schedulingType !== SchedulingType.ROUND_ROBIN,
}));
const { qualifiedRRHosts, allFallbackRRHosts, fixedHosts } = await findQualifiedHostsWithDwdCredentials({
eventType,
routedTeamMemberIds: routedTeamMemberIds || [],
rescheduleUid,
contactOwnerEmail,
routingFormResponse,
});
const { qualifiedRRHosts, allFallbackRRHosts, fixedHosts } =
await findQualifiedHostsWithDelegationCredentials({
eventType,
routedTeamMemberIds: routedTeamMemberIds || [],
rescheduleUid,
contactOwnerEmail,
routingFormResponse,
});
const allQualifiedHostsHashMap = [...qualifiedRRHosts, ...(allFallbackRRHosts ?? []), ...fixedHosts].reduce(
(acc, host) => {
if (host.user.id) {
@@ -141,14 +145,14 @@ export async function loadAndValidateUsers({
},
{} as {
[key: number]: Awaited<
ReturnType<typeof findQualifiedHostsWithDwdCredentials>
ReturnType<typeof findQualifiedHostsWithDelegationCredentials>
>["qualifiedRRHosts"][number];
}
);
let qualifiedRRUsers: UsersWithDwdCredentials = [];
let allFallbackRRUsers: UsersWithDwdCredentials = [];
let fixedUsers: UsersWithDwdCredentials = [];
let qualifiedRRUsers: UsersWithDelegationCredentials = [];
let allFallbackRRUsers: UsersWithDelegationCredentials = [];
let fixedUsers: UsersWithDelegationCredentials = [];
if (qualifiedRRHosts.length) {
// remove users that are not in the qualified hosts array
@@ -189,14 +193,14 @@ export async function loadAndValidateUsers({
memberId: firstUser.id ?? null,
teamId: eventType.teamId,
});
const usersEnrichedWithDwd = await enrichUsersWithDwdCredentials({
const usersEnrichedWithDelegationCredential = await enrichUsersWithDelegationCredentials({
orgId: firstUserOrgId ?? null,
users,
});
return {
qualifiedRRUsers,
additionalFallbackRRUsers, // without qualified
fixedUsers: usersEnrichedWithDwd,
fixedUsers: usersEnrichedWithDelegationCredential,
};
}
@@ -17,9 +17,9 @@ import {
mockSuccessfulVideoMeetingCreation,
mockCalendarToHaveNoBusySlots,
BookingLocations,
createDwdCredential,
createDelegationCredential,
createOrganization,
buildDwdCredential,
buildDelegationCredential,
} from "@calcom/web/test/utils/bookingScenario/bookingScenario";
import { createMockNextJsRequest } from "@calcom/web/test/utils/bookingScenario/createMockNextJsRequest";
import {
@@ -50,11 +50,11 @@ const timeout = process.env.CI ? 5000 : 20000;
describe("handleNewBooking", () => {
setupAndTeardown();
describe("Domain Wide Delegation", () => {
describe("Delegation Credential", () => {
test(
`should create a successful booking using the domain wide delegation credential
1. Should create a booking in the database with reference having DWD credential
2. Should create an event in calendar with DWD credential
`should create a successful booking using the delegation credential
1. Should create a booking in the database with reference having Delegation credential
2. Should create an event in calendar with Delegation credential
3. Should use Google Meet as the location even when not explicitly set.
`,
async ({ emails }) => {
@@ -90,14 +90,14 @@ describe("handleNewBooking", () => {
id: 101,
schedules: [TestData.schedules.IstWorkHours],
selectedCalendars: [TestData.selectedCalendars.google],
// User must be part of organization to be able to use that organization's DWD credential
// User must be part of organization to be able to use that organization's Delegation credential
teams: payloadToMakePartOfOrganization,
// No regular credentials provided
credentials: [],
destinationCalendar: TestData.selectedCalendars.google,
});
const dwd = await createDwdCredential(org.id);
const delegationCredential = await createDelegationCredential(org.id);
const payloadToCreateUserEventTypeForOrganizer = {
id: 1,
slotInterval: 30,
@@ -193,8 +193,8 @@ describe("handleNewBooking", () => {
meetingId: "GOOGLE_CALENDAR_EVENT_ID",
meetingPassword: "MOCK_PASSWORD",
meetingUrl: "https://GOOGLE_MEET_URL_IN_CALENDAR_EVENT",
// Verify DWD credential was used
domainWideDelegationCredentialId: dwd.id,
// Verify Delegation credential was used
delegationCredentialId: delegationCredential.id,
},
],
iCalUID: createdBooking.iCalUID,
@@ -202,7 +202,9 @@ describe("handleNewBooking", () => {
expectWorkflowToBeTriggered({ emailsToReceive: [organizer.email], emails });
expectSuccessfulCalendarEventCreationInCalendar(calendarMock, {
credential: buildDwdCredential({ serviceAccountKey: dwd.serviceAccountKey }),
credential: buildDelegationCredential({
serviceAccountKey: delegationCredential.serviceAccountKey,
}),
calendarId: TestData.selectedCalendars.google.externalId,
// There would be no videoCallUrl in this case as it is not a dedicated conferencing case and hangoutLink is used instead
videoCallUrl: null,
@@ -232,9 +234,9 @@ describe("handleNewBooking", () => {
);
test(
`should create a successful booking using the office365 domain wide delegation credential with Cal Video as the location
1. Should create a booking in the database with reference having DWD credential
2. Should create an event in Outlook calendar with DWD credential
`should create a successful booking using the office365 delegation credential with Cal Video as the location
1. Should create a booking in the database with reference having Delegation credential
2. Should create an event in Outlook calendar with Delegation credential
`,
async ({ emails }) => {
const handleNewBooking = (await import("@calcom/features/bookings/lib/handleNewBooking")).default;
@@ -269,14 +271,14 @@ describe("handleNewBooking", () => {
id: 101,
schedules: [TestData.schedules.IstWorkHours],
selectedCalendars: [TestData.selectedCalendars.office365],
// User must be part of organization to be able to use that organization's DWD credential
// User must be part of organization to be able to use that organization's Delegation credential
teams: payloadToMakePartOfOrganization,
// No regular credentials provided
credentials: [],
destinationCalendar: TestData.selectedCalendars.office365,
});
const dwd = await createDwdCredential(org.id, "office365");
const delegationCredential = await createDelegationCredential(org.id, "office365");
const payloadToCreateUserEventTypeForOrganizer = {
id: 1,
slotInterval: 30,
@@ -390,8 +392,8 @@ describe("handleNewBooking", () => {
meetingId: "OFFICE_365_CALENDAR_EVENT_ID",
meetingPassword: "MOCK_PASSWORD",
meetingUrl: "https://UNUSED_URL",
// Verify DWD credential was used
domainWideDelegationCredentialId: dwd.id,
// Verify Delegation credential was used
delegationCredentialId: delegationCredential.id,
},
],
iCalUID: createdBooking.iCalUID,
@@ -399,7 +401,9 @@ describe("handleNewBooking", () => {
expectWorkflowToBeTriggered({ emailsToReceive: [organizer.email], emails });
expectSuccessfulCalendarEventCreationInCalendar(calendarMock, {
credential: buildDwdCredential({ serviceAccountKey: dwd.serviceAccountKey }),
credential: buildDelegationCredential({
serviceAccountKey: delegationCredential.serviceAccountKey,
}),
calendarId: TestData.selectedCalendars.office365.externalId,
// There would be no videoCallUrl in this case as it is not a dedicated conferencing case and hangoutLink is used instead
videoCallUrl: null,
@@ -430,7 +434,7 @@ describe("handleNewBooking", () => {
);
test(
`should fail calendar event creation when organizer isn't part of the organization of DWD Credential`,
`should fail calendar event creation when organizer isn't part of the organization of DelegationCredential Credential`,
async () => {
const handleNewBooking = (await import("@calcom/features/bookings/lib/handleNewBooking")).default;
@@ -470,13 +474,13 @@ describe("handleNewBooking", () => {
id: 101,
schedules: [TestData.schedules.IstWorkHours],
selectedCalendars: [TestData.selectedCalendars.google],
// User must be part of organization to be able to use that organization's DWD credential
// User must be part of organization to be able to use that organization's Delegation credential
teams: payloadToMakePartOfOrganization,
// No regular credentials provided
credentials: [],
});
await createDwdCredential(anotherOrg.id);
await createDelegationCredential(anotherOrg.id);
const payloadToCreateUserEventTypeForOrganizer = {
id: 1,
slotInterval: 30,
@@ -598,9 +602,9 @@ describe("handleNewBooking", () => {
);
test(
`should create a successful dynamic group booking using the domain wide delegation credential
1. Should create a booking in the database with reference having DWD credential
2. Should create an event in calendar with DWD credential for both users
`should create a successful dynamic group booking using the delegation credential
1. Should create a booking in the database with reference having Delegation credential
2. Should create an event in calendar with Delegation credential for both users
3. Should use Google Meet as the location even when not explicitly set.
`,
async () => {
@@ -654,7 +658,7 @@ describe("handleNewBooking", () => {
destinationCalendar: TestData.selectedCalendars.google,
});
const dwd = await createDwdCredential(org.id);
const delegationCredential = await createDelegationCredential(org.id);
await createBookingScenario(
getScenarioData({
@@ -747,8 +751,8 @@ describe("handleNewBooking", () => {
meetingId: "GOOGLE_CALENDAR_EVENT_ID",
meetingPassword: "MOCK_PASSWORD",
meetingUrl: "https://GOOGLE_MEET_URL_IN_CALENDAR_EVENT",
// Verify DWD credential was used
domainWideDelegationCredentialId: dwd.id,
// Verify Delegation credential was used
delegationCredentialId: delegationCredential.id,
},
],
iCalUID: createdBooking.iCalUID,
@@ -4,8 +4,8 @@ import { getCalendar } from "@calcom/app-store/_utils/getCalendar";
import { sendCancelledSeatEmailsAndSMS } from "@calcom/emails";
import sendPayload from "@calcom/features/webhooks/lib/sendOrSchedulePayload";
import type { EventPayloadType, EventTypeInfo } from "@calcom/features/webhooks/lib/sendPayload";
import { getAllDwdCredentialsForUser } from "@calcom/lib/domainWideDelegation/server";
import { getDwdOrFindRegularCredential } from "@calcom/lib/domainWideDelegation/server";
import { getAllDelegationCredentialsForUser } from "@calcom/lib/delegationCredential/server";
import { getDelegationCredentialOrFindRegularCredential } from "@calcom/lib/delegationCredential/server";
import { HttpError } from "@calcom/lib/http-error";
import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
@@ -69,8 +69,8 @@ async function cancelAttendeeSeat(
const attendee = bookingToDelete?.attendees.find((attendee) => attendee.id === seatReference.attendeeId);
const bookingToDeleteUser = bookingToDelete.user ?? null;
const dwdCredentials = bookingToDeleteUser
? await getAllDwdCredentialsForUser({
const delegationCredentials = bookingToDeleteUser
? await getAllDelegationCredentialsForUser({
user: { email: bookingToDeleteUser.email, id: bookingToDeleteUser.id },
})
: [];
@@ -81,13 +81,13 @@ async function cancelAttendeeSeat(
const integrationsToUpdate = [];
for (const reference of bookingToDelete.references) {
if (reference.credentialId || reference.domainWideDelegationCredentialId) {
const credential = await getDwdOrFindRegularCredential({
if (reference.credentialId || reference.delegationCredentialId) {
const credential = await getDelegationCredentialOrFindRegularCredential({
id: {
credentialId: reference.credentialId,
domainWideDelegationCredentialId: reference.domainWideDelegationCredentialId,
delegationCredentialId: reference.delegationCredentialId,
},
dwdCredentials,
delegationCredentials,
});
if (credential) {
@@ -2,8 +2,8 @@ import type { Attendee } from "@prisma/client";
// eslint-disable-next-line no-restricted-imports
import { getCalendar } from "@calcom/app-store/_utils/getCalendar";
import { getAllDwdCredentialsForUser } from "@calcom/lib/domainWideDelegation/server";
import { getDwdOrFindRegularCredential } from "@calcom/lib/domainWideDelegation/server";
import { getAllDelegationCredentialsForUser } from "@calcom/lib/delegationCredential/server";
import { getDelegationCredentialOrFindRegularCredential } from "@calcom/lib/delegationCredential/server";
import { deleteMeeting } from "@calcom/lib/videoClient";
import prisma from "@calcom/prisma";
import { BookingStatus } from "@calcom/prisma/enums";
@@ -20,8 +20,8 @@ const lastAttendeeDeleteBooking = async (
) => {
let deletedReferences = false;
const bookingUser = originalRescheduledBooking?.user;
const dwdCredentials = bookingUser
? await getAllDwdCredentialsForUser({
const delegationCredentials = bookingUser
? await getAllDelegationCredentialsForUser({
user: { email: bookingUser.email, id: bookingUser.id },
})
: [];
@@ -29,13 +29,13 @@ const lastAttendeeDeleteBooking = async (
const integrationsToDelete = [];
for (const reference of originalRescheduledBooking.references) {
if (reference.credentialId || reference.domainWideDelegationCredentialId) {
const credential = await getDwdOrFindRegularCredential({
if (reference.credentialId || reference.delegationCredentialId) {
const credential = await getDelegationCredentialOrFindRegularCredential({
id: {
credentialId: reference.credentialId,
domainWideDelegationCredentialId: reference.domainWideDelegationCredentialId,
delegationCredentialId: reference.delegationCredentialId,
},
dwdCredentials,
delegationCredentials,
});
if (credential) {
@@ -17,7 +17,7 @@ export type ICalendarSwitchProps = {
isLastItemInList?: boolean;
destination?: boolean;
credentialId: number;
domainWideDelegationCredentialId: string | null;
delegationCredentialId: string | null;
eventTypeId: number | null;
disabled?: boolean;
};
@@ -36,7 +36,7 @@ const CalendarSwitch = (props: ICalendarSwitchProps) => {
isChecked,
name,
credentialId,
domainWideDelegationCredentialId,
delegationCredentialId,
eventTypeId,
disabled,
} = props;
@@ -48,7 +48,7 @@ const CalendarSwitch = (props: ICalendarSwitchProps) => {
const body = {
integration: type,
externalId: externalId,
...(domainWideDelegationCredentialId && { domainWideDelegationCredentialId }),
...(delegationCredentialId && { delegationCredentialId }),
// new URLSearchParams does not accept numbers
credentialId: String(credentialId),
...(eventTypeId ? { eventTypeId: String(eventTypeId) } : {}),
@@ -7,7 +7,7 @@ import { sendCancelledEmailsAndSMS } from "@calcom/emails";
import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventResponses";
import { deleteWebhookScheduledTriggers } from "@calcom/features/webhooks/lib/scheduleTrigger";
import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
import { buildNonDwdCredential } from "@calcom/lib/domainWideDelegation/server";
import { buildNonDelegationCredential } from "@calcom/lib/delegationCredential/server";
import { DailyLocationType } from "@calcom/lib/location";
import { deletePayment } from "@calcom/lib/payment/deletePayment";
import { getTranslation } from "@calcom/lib/server/i18n";
@@ -430,7 +430,7 @@ const handleDeleteCredential = async ({
// If it's a calendar remove it from the SelectedCalendars
if (credential.app?.categories.includes(AppCategories.calendar)) {
try {
const calendar = await getCalendar(buildNonDwdCredential(credential));
const calendar = await getCalendar(buildNonDelegationCredential(credential));
const calendars = await calendar?.listCalendars();

Some files were not shown because too many files have changed in this diff Show More