Back to Cal

Cal.com Permission Documentation

PERMISSIONS.md

6.2.010.3 KB
Original Source

Cal.com Permission Documentation

This document maps existing role-based permission checks to the new PBAC (Permission-Based Access Control) system's permission strings in the format resource.action.

Permissions by Resource

Team Permissions

Permission StringDescriptionFile PathLine
team.createCreate teamspackages/trpc/server/routers/viewer/teams/create.handler.ts55-57
team.updateUpdate team settingspackages/trpc/server/routers/viewer/teams/update.handler.ts18
team.changeMemberRoleChange role of team memberspackages/trpc/server/routers/viewer/teams/changeMemberRole.handler.ts18-21
team.removeRemove members from teampackages/trpc/server/routers/viewer/teams/removeMember.handler.ts35-36, 51-55
team.inviteInvite members to teampackages/trpc/server/routers/viewer/teams/invite.handler.ts-

Event Type Permissions

Permission StringDescriptionFile PathLine
eventType.createCreate event typespackages/trpc/server/routers/viewer/eventTypes/create.handler.ts-
eventType.updateUpdate event typespackages/trpc/server/routers/viewer/eventTypes/update.handler.ts162-164
eventType.deleteDelete event typespackages/trpc/server/routers/viewer/eventTypes/delete.handler.ts13-31

Booking Permissions

Permission StringDescriptionFile PathLine
booking.readRead booking detailspackages/trpc/server/routers/viewer/bookings/get.handler.ts95-107, 132-134
booking.readTeamBookingsRead team bookingspackages/trpc/server/routers/viewer/bookings/get.handler.ts240-252
booking.readOrgBookingsRead organization bookingspackages/trpc/server/routers/viewer/bookings/get.handler.ts240-252

Organization Permissions

Permission StringDescriptionFile PathLine
organization.readRead organization detailspackages/trpc/server/routers/viewer/organizations/get.handler.ts-
organization.listMembersList organization memberspackages/trpc/server/routers/viewer/organizations/listMembers.handler.ts68-76
organization.createCreate organizationpackages/trpc/server/routers/viewer/organizations/create.handler.ts-

API Key Permissions

Permission StringDescriptionFile PathLine
apiKey.createCreate API keyspackages/trpc/server/routers/viewer/apiKeys/create.handler.ts25-26
apiKey.findKeyOfTypeFind API keys by typepackages/trpc/server/routers/viewer/apiKeys/findKeyOfType.handler.ts18-19

Helper Functions and Utilities

The following helper functions are commonly used for permission checks:

FunctionDescriptionFile Path
isTeamAdminChecks if user is admin or owner of a teampackages/lib/server/queries/teams/index.ts
isTeamOwnerChecks if user is owner of a teampackages/lib/server/queries/teams/index.ts
isTeamMemberChecks if user is a member of a teampackages/lib/server/queries/teams/index.ts
canEditEntityChecks if user can edit an entitypackages/lib/entityPermissionUtils.ts
canAccessEntityChecks if user can access an entitypackages/lib/entityPermissionUtils.ts
getEntityPermissionLevelGets permission level for an entitypackages/lib/entityPermissionUtils.ts
canCreateEntityChecks if user can create an entitypackages/lib/entityPermissionUtils.ts
withRoleCanCreateEntityChecks if role allows entity creationpackages/lib/entityPermissionUtils.ts

Permission Level Enums

The codebase uses several enums to define permission levels:

MembershipRole Enum

typescript
enum MembershipRole {
  OWNER = "OWNER",
  ADMIN = "ADMIN",
  MEMBER = "MEMBER"
}

Entity Permission Level Enum

typescript
enum ENTITY_PERMISSION_LEVEL {
  NONE,
  USER_ONLY_WRITE,
  TEAM_READ_ONLY,
  TEAM_WRITE
}

Common Permission Patterns

  1. Team Admin/Owner Check: Many operations require the user to be a team admin or owner.

    typescript
    if (!(await isTeamAdmin(ctx.user?.id, input.teamId))) throw new TRPCError({ code: "UNAUTHORIZED" });
    
  2. Team Owner Check: Some operations (like changing an owner's role) require the user to be a team owner.

    typescript
    if (input.role === MembershipRole.OWNER && !(await isTeamOwner(ctx.user?.id, input.teamId)))
      throw new TRPCError({ code: "UNAUTHORIZED" });
    
  3. Organization Admin Check: Operations within an organization require the user to be an organization admin.

    typescript
    if (user.profile?.organizationId && !user.organization.isOrgAdmin) {
      throw new TRPCError({ code: "FORBIDDEN", message: "org_admins_can_create_new_teams" });
    }
    
  4. Entity Permission Check: Entity operations use permission level checks.

    typescript
    const permissionLevel = await getEntityPermissionLevel(entity, userId);
    return permissionLevel === ENTITY_PERMISSION_LEVEL.TEAM_WRITE || 
           permissionLevel === ENTITY_PERMISSION_LEVEL.USER_ONLY_WRITE;
    
  5. Booking Access Control: Complex permission checks for retrieving bookings based on user roles and team/organization membership.

    typescript
    const membershipIdsWhereUserIsAdminOwner = (
      await prisma.membership.findMany({
        where: {
          userId: user.id,
          role: {
            in: ["ADMIN", "OWNER"],
          },
        },
        select: {
          id: true,
        },
      })
    ).map((membership) => membership.id);
    

Migration to PBAC

When migrating to the new PBAC system, these existing role-based checks should be replaced with permission string checks using the permissionMatches function:

typescript
import { permissionMatches } from "@calcom/features/pbac/types/permission-registry";

// Instead of:
if (!(await isTeamAdmin(ctx.user?.id, input.teamId))) throw new TRPCError({ code: "UNAUTHORIZED" });

// Use:
if (!permissionMatches("team.update", userPermissions)) throw new TRPCError({ code: "UNAUTHORIZED" });

Permission String Alternatives for Helper Functions

The following table provides permission string alternatives for common helper functions:

Helper FunctionPermission String AlternativeDescription
isTeamAdminteam.*Grants all team permissions
isTeamAdminteam.updateUpdate team settings
isTeamAdminteam.inviteInvite team members
isTeamAdminteam.removeRemove team members
isTeamOwnerteam.changeMemberRoleChange role of team members
isTeamOwnerteam.deleteDelete team
isTeamMemberteam.readRead-only access to team

Entity Permission Functions and Resources

The following functions are used to check permissions for entities with userId and teamId properties:

FunctionDescriptionPermission String Alternative
canEditEntityChecks if user can edit an entity{resource}.update
canAccessEntityChecks if user can access an entity{resource}.read
getEntityPermissionLevelGets permission level for an entityN/A - Implementation detail
canCreateEntityChecks if user can create an entity{resource}.create

Resources Used with Entity Permission Functions

ResourcecanEditEntity UsagecanAccessEntity UsagePermission String
routingFormpackages/app-store/routing-forms/trpc/forms.handler.tspackages/app-store/routing-forms/trpc/getResponseWithFormFields.handler.tsroutingForm.update, routingForm.read
routingFormpackages/app-store/routing-forms/trpc/formMutation.handler.tsroutingForm.update
routingFormpackages/app-store/routing-forms/api/responses/[formId].tsroutingForm.update

These functions can be used with any entity that has userId and teamId properties. When migrating to PBAC, replace these checks with appropriate permission string checks:

typescript
// Instead of:
if (!(await canEditEntity(form, user.id))) throw new TRPCError({ code: "UNAUTHORIZED" });

// Use:
if (!permissionMatches("routingForm.update", userPermissions)) throw new TRPCError({ code: "UNAUTHORIZED" });

This allows for more granular permission control and the creation of custom roles with specific permissions.