Skip to content

Commit

Permalink
Merge branch 'dev' into contact-channel-api
Browse files Browse the repository at this point in the history
  • Loading branch information
fomalhautb committed Oct 1, 2024
2 parents 6985443 + 28c3f57 commit edda2b0
Show file tree
Hide file tree
Showing 63 changed files with 403 additions and 471 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ pnpm install
pnpm run build

# reset & start the dependencies (DB, Inbucket, etc.) as Docker containers, seeding the DB with the Prisma schema
pnpm run restart-deps
# pnpm run start-deps
pnpm run start-deps
# pnpm run restart-deps
# pnpm run stop-deps

# Start the dev server
Expand Down
8 changes: 8 additions & 0 deletions apps/backend/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# @stackframe/stack-backend

## 2.6.1

### Patch Changes

- Bugfixes
- @stackframe/stack-emails@2.6.1
- @stackframe/stack-shared@2.6.1

## 2.6.0

### Minor Changes
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@stackframe/stack-backend",
"version": "2.6.0",
"version": "2.6.1",
"private": true,
"scripts": {
"clean": "rimraf .next && rimraf node_modules",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import { usersCrudHandlers } from "@/app/api/v1/users/crud";
import { getProvider } from "@/oauth";
import { prismaClient } from "@/prisma-client";
import { createCrudHandlers } from "@/route-handlers/crud-handler";
import { getIdFromUserIdOrMe } from "@/route-handlers/utils";
import { KnownErrors } from "@stackframe/stack-shared";
import { connectedAccountAccessTokenCrud } from "@stackframe/stack-shared/dist/interface/crud/oauth";
import { yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields";
import { userIdOrMeSchema, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields";
import { StackAssertionError, StatusError } from "@stackframe/stack-shared/dist/utils/errors";
import { createLazyProxy } from "@stackframe/stack-shared/dist/utils/proxies";
import { extractScopes } from "@stackframe/stack-shared/dist/utils/strings";
Expand All @@ -14,12 +13,10 @@ import { extractScopes } from "@stackframe/stack-shared/dist/utils/strings";
export const connectedAccountAccessTokenCrudHandlers = createLazyProxy(() =>createCrudHandlers(connectedAccountAccessTokenCrud, {
paramsSchema: yupObject({
provider_id: yupString().required(),
user_id: yupString().required(),
user_id: userIdOrMeSchema.required(),
}),
async onCreate({ auth, data, params }) {
const userId = getIdFromUserIdOrMe(params.user_id, auth.user);

if (auth.type === 'client' && auth.user?.id !== userId) {
if (auth.type === 'client' && auth.user?.id !== params.user_id) {
throw new StatusError(StatusError.Forbidden, "Client can only access its own connected accounts");
}

Expand All @@ -32,7 +29,7 @@ export const connectedAccountAccessTokenCrudHandlers = createLazyProxy(() =>crea
throw new KnownErrors.OAuthAccessTokenNotAvailableWithSharedOAuthKeys();
}

const user = await usersCrudHandlers.adminRead({ project: auth.project, user_id: userId });
const user = await usersCrudHandlers.adminRead({ project: auth.project, user_id: params.user_id });
if (!user.oauth_providers.map(x => x.id).includes(params.provider_id)) {
throw new KnownErrors.OAuthConnectionNotConnectedToUser();
}
Expand All @@ -44,7 +41,7 @@ export const connectedAccountAccessTokenCrudHandlers = createLazyProxy(() =>crea
projectId: auth.project.id,
oAuthProviderConfigId: params.provider_id,
projectUserOAuthAccount: {
projectUserId: userId,
projectUserId: params.user_id,
},
expiresAt: {
// is at least 5 minutes in the future
Expand All @@ -66,7 +63,7 @@ export const connectedAccountAccessTokenCrudHandlers = createLazyProxy(() =>crea
projectId: auth.project.id,
oAuthProviderConfigId: params.provider_id,
projectUserOAuthAccount: {
projectUserId: userId,
projectUserId: params.user_id,
}
},
});
Expand Down
28 changes: 11 additions & 17 deletions apps/backend/src/app/api/v1/team-member-profiles/crud.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { ensureTeamExist, ensureTeamMembershipExists, ensureUserExist, ensureUserTeamPermissionExists } from "@/lib/request-checks";
import { prismaClient } from "@/prisma-client";
import { createCrudHandlers } from "@/route-handlers/crud-handler";
import { getIdFromUserIdOrMe } from "@/route-handlers/utils";
import { Prisma } from "@prisma/client";
import { KnownErrors } from "@stackframe/stack-shared";
import { teamMemberProfilesCrud } from "@stackframe/stack-shared/dist/interface/crud/team-member-profiles";
Expand Down Expand Up @@ -33,7 +32,6 @@ export const teamMemberProfilesCrudHandlers = createLazyProxy(() => createCrudHa
}),
onList: async ({ auth, query }) => {
return await prismaClient.$transaction(async (tx) => {
const userId = getIdFromUserIdOrMe(query.user_id, auth.user);
if (auth.type === 'client') {
// Client can only:
// - list users in their own team if they have the $read_members permission
Expand All @@ -47,7 +45,7 @@ export const teamMemberProfilesCrudHandlers = createLazyProxy(() => createCrudHa

await ensureTeamMembershipExists(tx, { projectId: auth.project.id, teamId: query.team_id, userId: currentUserId });

if (userId !== currentUserId) {
if (query.user_id !== currentUserId) {
await ensureUserTeamPermissionExists(tx, {
project: auth.project,
teamId: query.team_id,
Expand All @@ -61,16 +59,16 @@ export const teamMemberProfilesCrudHandlers = createLazyProxy(() => createCrudHa
if (query.team_id) {
await ensureTeamExist(tx, { projectId: auth.project.id, teamId: query.team_id });
}
if (userId) {
await ensureUserExist(tx, { projectId: auth.project.id, userId: userId });
if (query.user_id) {
await ensureUserExist(tx, { projectId: auth.project.id, userId: query.user_id });
}
}

const db = await tx.teamMember.findMany({
where: {
projectId: auth.project.id,
teamId: query.team_id,
projectUserId: userId,
projectUserId: query.user_id,
},
orderBy: {
createdAt: 'asc',
Expand All @@ -88,11 +86,9 @@ export const teamMemberProfilesCrudHandlers = createLazyProxy(() => createCrudHa
},
onRead: async ({ auth, params }) => {
return await prismaClient.$transaction(async (tx) => {
const userId = getIdFromUserIdOrMe(params.user_id, auth.user);

if (auth.type === 'client') {
const currentUserId = auth.user?.id ?? throwErr(new KnownErrors.CannotGetOwnUserWithoutUser());
if (userId !== currentUserId) {
if (params.user_id !== currentUserId) {
await ensureUserTeamPermissionExists(tx, {
project: auth.project,
teamId: params.team_id,
Expand All @@ -104,13 +100,13 @@ export const teamMemberProfilesCrudHandlers = createLazyProxy(() => createCrudHa
}
}

await ensureTeamMembershipExists(tx, { projectId: auth.project.id, teamId: params.team_id, userId: userId });
await ensureTeamMembershipExists(tx, { projectId: auth.project.id, teamId: params.team_id, userId: params.user_id });

const db = await tx.teamMember.findUnique({
where: {
projectId_projectUserId_teamId: {
projectId: auth.project.id,
projectUserId: userId,
projectUserId: params.user_id,
teamId: params.team_id,
},
},
Expand All @@ -119,34 +115,32 @@ export const teamMemberProfilesCrudHandlers = createLazyProxy(() => createCrudHa

if (!db) {
// This should never happen because of the check above
throw new KnownErrors.TeamMembershipNotFound(params.team_id, userId);
throw new KnownErrors.TeamMembershipNotFound(params.team_id, params.user_id);
}

return prismaToCrud(db, await getUserLastActiveAtMillis(db.projectUser.projectUserId, db.projectUser.createdAt));
});
},
onUpdate: async ({ auth, data, params }) => {
return await prismaClient.$transaction(async (tx) => {
const userId = getIdFromUserIdOrMe(params.user_id, auth.user);

if (auth.type === 'client') {
const currentUserId = auth.user?.id ?? throwErr(new KnownErrors.CannotGetOwnUserWithoutUser());
if (userId !== currentUserId) {
if (params.user_id !== currentUserId) {
throw new StatusError(StatusError.Forbidden, 'Cannot update another user\'s profile');
}
}

await ensureTeamMembershipExists(tx, {
projectId: auth.project.id,
teamId: params.team_id,
userId,
userId: params.user_id,
});

const db = await tx.teamMember.update({
where: {
projectId_projectUserId_teamId: {
projectId: auth.project.id,
projectUserId: userId,
projectUserId: params.user_id,
teamId: params.team_id,
},
},
Expand Down
19 changes: 7 additions & 12 deletions apps/backend/src/app/api/v1/team-memberships/crud.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { ensureTeamExist, ensureTeamMembershipExists, ensureTeamMembershipDoesNo
import { isTeamSystemPermission, teamSystemPermissionStringToDBType } from "@/lib/permissions";
import { prismaClient } from "@/prisma-client";
import { createCrudHandlers } from "@/route-handlers/crud-handler";
import { getIdFromUserIdOrMe } from "@/route-handlers/utils";
import { teamMembershipsCrud } from "@stackframe/stack-shared/dist/interface/crud/team-memberships";
import { userIdOrMeSchema, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields";
import { throwErr } from "@stackframe/stack-shared/dist/utils/errors";
Expand Down Expand Up @@ -57,8 +56,6 @@ export const teamMembershipsCrudHandlers = createLazyProxy(() => createCrudHandl
user_id: userIdOrMeSchema.required(),
}),
onCreate: async ({ auth, params }) => {
const userId = getIdFromUserIdOrMe(params.user_id, auth.user);

await prismaClient.$transaction(async (tx) => {
await ensureTeamExist(tx, {
projectId: auth.project.id,
Expand All @@ -68,14 +65,14 @@ export const teamMembershipsCrudHandlers = createLazyProxy(() => createCrudHandl
await ensureTeamMembershipDoesNotExist(tx, {
projectId: auth.project.id,
teamId: params.team_id,
userId,
userId: params.user_id
});

const user = await tx.projectUser.findUnique({
where: {
projectId_projectUserId: {
projectId: auth.project.id,
projectUserId: userId,
projectUserId: params.user_id,
},
},
});
Expand All @@ -87,14 +84,14 @@ export const teamMembershipsCrudHandlers = createLazyProxy(() => createCrudHandl
await addUserToTeam(tx, {
project: auth.project,
teamId: params.team_id,
userId,
userId: params.user_id,
type: 'member',
});
});

const data = {
team_id: params.team_id,
user_id: userId,
user_id: params.user_id,
};

await sendTeamMembershipCreatedWebhook({
Expand All @@ -105,15 +102,13 @@ export const teamMembershipsCrudHandlers = createLazyProxy(() => createCrudHandl
return data;
},
onDelete: async ({ auth, params }) => {
const userId = getIdFromUserIdOrMe(params.user_id, auth.user);

await prismaClient.$transaction(async (tx) => {
// Users are always allowed to remove themselves from a team
// Only users with the $remove_members permission can remove other users
if (auth.type === 'client') {
const currentUserId = auth.user?.id ?? throwErr(new KnownErrors.CannotGetOwnUserWithoutUser());

if (userId !== currentUserId) {
if (params.user_id !== currentUserId) {
await ensureUserTeamPermissionExists(tx, {
project: auth.project,
teamId: params.team_id,
Expand All @@ -128,7 +123,7 @@ export const teamMembershipsCrudHandlers = createLazyProxy(() => createCrudHandl
await ensureTeamMembershipExists(tx, {
projectId: auth.project.id,
teamId: params.team_id,
userId,
userId: params.user_id,
});

await tx.teamMember.delete({
Expand All @@ -146,7 +141,7 @@ export const teamMembershipsCrudHandlers = createLazyProxy(() => createCrudHandl
projectId: auth.project.id,
data: {
team_id: params.team_id,
user_id: userId,
user_id: params.user_id,
},
});
},
Expand Down
6 changes: 2 additions & 4 deletions apps/backend/src/app/api/v1/team-permissions/crud.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { grantTeamPermission, listUserTeamPermissions, revokeTeamPermission } fr
import { ensureTeamMembershipExists, ensureUserTeamPermissionExists } from "@/lib/request-checks";
import { prismaClient } from "@/prisma-client";
import { createCrudHandlers } from "@/route-handlers/crud-handler";
import { getIdFromUserIdOrMe } from "@/route-handlers/utils";
import { KnownErrors } from "@stackframe/stack-shared";
import { teamPermissionsCrud } from '@stackframe/stack-shared/dist/interface/crud/team-permissions';
import { teamPermissionDefinitionIdSchema, userIdOrMeSchema, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields";
Expand Down Expand Up @@ -53,11 +52,10 @@ export const teamPermissionsCrudHandlers = createLazyProxy(() => createCrudHandl
});
},
async onList({ auth, query }) {
const userId = getIdFromUserIdOrMe(query.user_id, auth.user);
if (auth.type === 'client') {
const currentUserId = auth.user?.id || throwErr(new KnownErrors.CannotGetOwnUserWithoutUser());

if (userId !== currentUserId) {
if (query.user_id !== currentUserId) {
throw new StatusError(StatusError.Forbidden, 'Client can only list permissions for their own user. user_id must be either "me" or the ID of the current user');
}
}
Expand All @@ -68,7 +66,7 @@ export const teamPermissionsCrudHandlers = createLazyProxy(() => createCrudHandl
project: auth.project,
teamId: query.team_id,
permissionId: query.permission_id,
userId,
userId: query.user_id,
recursive: query.recursive === 'true',
}),
is_paginated: false,
Expand Down
Loading

0 comments on commit edda2b0

Please sign in to comment.