Skip to content

Commit

Permalink
More changes
Browse files Browse the repository at this point in the history
  • Loading branch information
N2D4 committed Feb 6, 2025
1 parent 7c531f5 commit f77cd05
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 82 deletions.
14 changes: 12 additions & 2 deletions apps/backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ model Project {
projectUsers ProjectUser[]
neonProvisionedProject NeonProvisionedProject?
tenancies Tenancy[]
verificationCodes VerificationCode[]
}

model ProjectConfig {
Expand Down Expand Up @@ -96,6 +97,11 @@ model Team {
tenancyId String @db.Uuid
teamId String @default(uuid()) @db.Uuid
// Team IDs must be unique across all organizations (but not necessarily across all branches).
// To model this in the DB, we add two columns that are always equal to tenancy.projectId and tenancy.branchId.
mirroredProjectId String
mirroredBranchId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Expand All @@ -110,6 +116,7 @@ model Team {
teamMembers TeamMember[]
@@id([tenancyId, teamId])
@@unique([mirroredProjectId, mirroredBranchId, teamId])
}

// This is used for fields that are boolean but only the true value is part of a unique constraint.
Expand Down Expand Up @@ -701,8 +708,11 @@ model ProjectUserAuthorizationCode {

model VerificationCode {
projectId String
branchId String
id String @default(uuid()) @db.Uuid
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Expand All @@ -715,8 +725,8 @@ model VerificationCode {
data Json
attemptCount Int @default(0)
@@id([projectId, id])
@@unique([projectId, code])
@@id([projectId, branchId, id])
@@unique([projectId, branchId, code])
@@index([data(ops: JsonbPathOps)], type: Gin)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getProject } from "@/lib/projects";
import { getDefaultTenancyFromProject } from "@/lib/tenancies";
import { prismaClient } from "@/prisma-client";
import { createSmartRouteHandler } from "@/route-handlers/smart-route-handler";
import { neonAuthorizationHeaderSchema, urlSchema, yupNumber, yupObject, yupString, yupTuple } from "@stackframe/stack-shared/dist/schema-fields";
Expand Down Expand Up @@ -42,7 +43,7 @@ export const POST = createSmartRouteHandler({
}

const transferCodeObj = await neonIntegrationProjectTransferCodeHandler.createCode({
project: internalProject,
tenancy: await getDefaultTenancyFromProject(internalProject.id),
method: {},
data: {
project_id: neonProvisionedProject.projectId,
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/src/app/api/latest/projects/current/crud.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const projectsCrudHandlers = createLazyProxy(() => createCrudHandlers(pro
},
] as const;

const permissions = await listTeamPermissionDefinitions(tx, oldProject);
const permissions = await listTeamPermissionDefinitions(tx, auth.tenancy);


for (const param of dbParams) {
Expand Down
6 changes: 5 additions & 1 deletion apps/backend/src/app/api/latest/users/crud.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,11 @@ export function getUserQuery(tenancyId: string, userId: string): RawQuery<UsersC
};
}

export async function getUser(options: { tenancyId: string, userId: string }) {
export async function getUser(options: { userId: string } & ({ projectId: string, branchId: string } | { tenancyId: string })) {
if ("projectId" in options) {
const tenancy = await getDefaultTenancyFromProject(options.projectId);
}

const result = await rawQuery(getUserQuery(options.tenancyId, options.userId));

// In non-prod environments, let's also call the legacy function and ensure the result is the same
Expand Down
74 changes: 37 additions & 37 deletions apps/backend/src/lib/permissions.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { TeamSystemPermission as DBTeamSystemPermission, Prisma } from "@prisma/client";
import { KnownErrors } from "@stackframe/stack-shared";
import { ProjectsCrud } from "@stackframe/stack-shared/dist/interface/crud/projects";
import { TeamPermissionDefinitionsCrud, TeamPermissionsCrud } from "@stackframe/stack-shared/dist/interface/crud/team-permissions";
import { StackAssertionError, throwErr } from "@stackframe/stack-shared/dist/utils/errors";
import { stringCompare, typedToLowercase, typedToUppercase } from "@stackframe/stack-shared/dist/utils/strings";
import { Tenancy } from "./tenancies";
import { PrismaTransaction } from "./types";

export const fullPermissionInclude = {
Expand Down Expand Up @@ -90,12 +90,12 @@ export function teamPermissionDefinitionJsonFromTeamSystemDbType(db: DBTeamSyste
async function getParentDbIds(
tx: PrismaTransaction,
options: {
project: ProjectsCrud["Admin"]["Read"],
tenancy: Tenancy,
containedPermissionIds?: string[],
}
) {
let parentDbIds = [];
const potentialParentPermissions = await listTeamPermissionDefinitions(tx, options.project);
const potentialParentPermissions = await listTeamPermissionDefinitions(tx, options.tenancy);
for (const parentPermissionId of options.containedPermissionIds || []) {
const parentPermission = potentialParentPermissions.find(p => p.id === parentPermissionId);
if (!parentPermission) {
Expand All @@ -111,18 +111,18 @@ async function getParentDbIds(
export async function listUserTeamPermissions(
tx: PrismaTransaction,
options: {
project: ProjectsCrud["Admin"]["Read"],
tenancy: Tenancy,
teamId?: string,
userId?: string,
permissionId?: string,
recursive: boolean,
}
): Promise<TeamPermissionsCrud["Admin"]["Read"][]> {
const permissionDefs = await listTeamPermissionDefinitions(tx, options.project);
const permissionDefs = await listTeamPermissionDefinitions(tx, options.tenancy);
const permissionsMap = new Map(permissionDefs.map(p => [p.id, p]));
const results = await tx.teamMemberDirectPermission.findMany({
where: {
projectId: options.project.id,
tenancyId: options.tenancy.id,
projectUserId: options.userId,
teamId: options.teamId,
},
Expand Down Expand Up @@ -174,7 +174,7 @@ export async function listUserTeamPermissions(
export async function grantTeamPermission(
tx: PrismaTransaction,
options: {
project: ProjectsCrud["Admin"]["Read"],
tenancy: Tenancy,
teamId: string,
userId: string,
permissionId: string,
Expand All @@ -183,8 +183,8 @@ export async function grantTeamPermission(
if (isTeamSystemPermission(options.permissionId)) {
await tx.teamMemberDirectPermission.upsert({
where: {
projectId_projectUserId_teamId_systemPermission: {
projectId: options.project.id,
tenancyId_projectUserId_teamId_systemPermission: {
tenancyId: options.tenancy.id,
projectUserId: options.userId,
teamId: options.teamId,
systemPermission: teamSystemPermissionStringToDBType(options.permissionId),
Expand All @@ -194,8 +194,8 @@ export async function grantTeamPermission(
systemPermission: teamSystemPermissionStringToDBType(options.permissionId),
teamMember: {
connect: {
projectId_projectUserId_teamId: {
projectId: options.project.id,
tenancyId_projectUserId_teamId: {
tenancyId: options.tenancy.id,
projectUserId: options.userId,
teamId: options.teamId,
},
Expand All @@ -207,8 +207,8 @@ export async function grantTeamPermission(
} else {
const teamSpecificPermission = await tx.permission.findUnique({
where: {
projectId_teamId_queryableId: {
projectId: options.project.id,
tenancyId_teamId_queryableId: {
tenancyId: options.tenancy.id,
teamId: options.teamId,
queryableId: options.permissionId,
},
Expand All @@ -217,7 +217,7 @@ export async function grantTeamPermission(
const anyTeamPermission = await tx.permission.findUnique({
where: {
projectConfigId_queryableId: {
projectConfigId: options.project.config.id,
projectConfigId: options.tenancy.config.id,
queryableId: options.permissionId,
},
}
Expand All @@ -228,8 +228,8 @@ export async function grantTeamPermission(

await tx.teamMemberDirectPermission.upsert({
where: {
projectId_projectUserId_teamId_permissionDbId: {
projectId: options.project.id,
tenancyId_projectUserId_teamId_permissionDbId: {
tenancyId: options.tenancy.id,
projectUserId: options.userId,
teamId: options.teamId,
permissionDbId: permission.dbId,
Expand All @@ -243,8 +243,8 @@ export async function grantTeamPermission(
},
teamMember: {
connect: {
projectId_projectUserId_teamId: {
projectId: options.project.id,
tenancyId_projectUserId_teamId: {
tenancyId: options.tenancy.id,
projectUserId: options.userId,
teamId: options.teamId,
},
Expand All @@ -265,7 +265,7 @@ export async function grantTeamPermission(
export async function revokeTeamPermission(
tx: PrismaTransaction,
options: {
project: ProjectsCrud["Admin"]["Read"],
tenancy: Tenancy,
teamId: string,
userId: string,
permissionId: string,
Expand All @@ -274,8 +274,8 @@ export async function revokeTeamPermission(
if (isTeamSystemPermission(options.permissionId)) {
await tx.teamMemberDirectPermission.delete({
where: {
projectId_projectUserId_teamId_systemPermission: {
projectId: options.project.id,
tenancyId_projectUserId_teamId_systemPermission: {
tenancyId: options.tenancy.id,
projectUserId: options.userId,
teamId: options.teamId,
systemPermission: teamSystemPermissionStringToDBType(options.permissionId),
Expand All @@ -287,8 +287,8 @@ export async function revokeTeamPermission(
} else {
const teamSpecificPermission = await tx.permission.findUnique({
where: {
projectId_teamId_queryableId: {
projectId: options.project.id,
tenancyId_teamId_queryableId: {
tenancyId: options.tenancy.id,
teamId: options.teamId,
queryableId: options.permissionId,
},
Expand All @@ -297,7 +297,7 @@ export async function revokeTeamPermission(
const anyTeamPermission = await tx.permission.findUnique({
where: {
projectConfigId_queryableId: {
projectConfigId: options.project.config.id,
projectConfigId: options.tenancy.config.id,
queryableId: options.permissionId,
},
}
Expand All @@ -308,8 +308,8 @@ export async function revokeTeamPermission(

await tx.teamMemberDirectPermission.delete({
where: {
projectId_projectUserId_teamId_permissionDbId: {
projectId: options.project.id,
tenancyId_projectUserId_teamId_permissionDbId: {
tenancyId: options.tenancy.id,
projectUserId: options.userId,
teamId: options.teamId,
permissionDbId: permission.dbId,
Expand All @@ -322,19 +322,19 @@ export async function revokeTeamPermission(

export async function listTeamPermissionDefinitions(
tx: PrismaTransaction,
project: ProjectsCrud["Admin"]["Read"]
tenancy: Tenancy
): Promise<(TeamPermissionDefinitionsCrud["Admin"]["Read"] & { __database_id: string })[]> {
const projectConfig = await tx.projectConfig.findUnique({
where: {
id: project.config.id,
id: tenancy.config.id,
},
include: {
permissions: {
include: fullPermissionInclude,
},
},
});
if (!projectConfig) throw new StackAssertionError(`Couldn't find project config`, { project });
if (!projectConfig) throw new StackAssertionError(`Couldn't find tenancy config`, { tenancy });
const res = projectConfig.permissions;
const nonSystemPermissions = res.map(db => teamPermissionDefinitionJsonFromDbType(db));

Expand All @@ -346,7 +346,7 @@ export async function listTeamPermissionDefinitions(
export async function createTeamPermissionDefinition(
tx: PrismaTransaction,
options: {
project: ProjectsCrud["Admin"]["Read"],
tenancy: Tenancy,
data: {
id: string,
description?: string,
Expand All @@ -355,15 +355,15 @@ export async function createTeamPermissionDefinition(
}
) {
const parentDbIds = await getParentDbIds(tx, {
project: options.project,
tenancy: options.tenancy,
containedPermissionIds: options.data.contained_permission_ids
});
const dbPermission = await tx.permission.create({
data: {
scope: "TEAM",
queryableId: options.data.id,
description: options.data.description,
projectConfigId: options.project.config.id,
projectConfigId: options.tenancy.config.id,
parentEdges: {
create: parentDbIds.map(parentDbId => {
if (isTeamSystemPermission(parentDbId)) {
Expand All @@ -390,7 +390,7 @@ export async function createTeamPermissionDefinition(
export async function updateTeamPermissionDefinitions(
tx: PrismaTransaction,
options: {
project: ProjectsCrud["Admin"]["Read"],
tenancy: Tenancy,
permissionId: string,
data: {
id?: string,
Expand All @@ -400,7 +400,7 @@ export async function updateTeamPermissionDefinitions(
}
) {
const parentDbIds = await getParentDbIds(tx, {
project: options.project,
tenancy: options.tenancy,
containedPermissionIds: options.data.contained_permission_ids
});

Expand Down Expand Up @@ -431,7 +431,7 @@ export async function updateTeamPermissionDefinitions(
const db = await tx.permission.update({
where: {
projectConfigId_queryableId: {
projectConfigId: options.project.config.id,
projectConfigId: options.tenancy.config.id,
queryableId: options.permissionId,
},
scope: "TEAM",
Expand All @@ -449,13 +449,13 @@ export async function updateTeamPermissionDefinitions(
export async function deleteTeamPermissionDefinition(
tx: PrismaTransaction,
options: {
project: ProjectsCrud["Admin"]["Read"],
tenancy: Tenancy,
permissionId: string,
}
) {
const deleted = await tx.permission.deleteMany({
where: {
projectConfigId: options.project.config.id,
projectConfigId: options.tenancy.config.id,
queryableId: options.permissionId,
scope: "TEAM",
},
Expand Down
Loading

0 comments on commit f77cd05

Please sign in to comment.