diff --git a/packages/api/src/controllers/api-token.js b/packages/api/src/controllers/api-token.js index 00d8ab7d64..8955e83b24 100644 --- a/packages/api/src/controllers/api-token.js +++ b/packages/api/src/controllers/api-token.js @@ -2,23 +2,15 @@ import Router from "express/lib/router"; import { v4 as uuid } from "uuid"; import sql from "sql-template-strings"; -import { - makeNextHREF, - parseOrder, - parseFilters, - addDefaultProjectId, -} from "./helpers"; +import { makeNextHREF, parseOrder, parseFilters } from "./helpers"; import { authorizer, validatePost } from "../middleware"; import { AuthPolicy } from "../middleware/authPolicy"; import { db } from "../store"; -import mung from "express-mung"; const app = Router(); app.use(authorizer({ noApiToken: true })); -app.use(mung.jsonAsync(addDefaultProjectId)); - app.get("/:id", async (req, res) => { const { id } = req.params; const apiToken = await req.store.get(`api-token/${id}`); @@ -75,6 +67,10 @@ app.get("/", async (req, res) => { if (!userId) { const query = parseFilters(fieldsMap, filters); + query.push( + sql`coalesce(api_token.data->>'projectId', '') = ${req.project?.id || ""}` + ); + let fields = " api_token.id as id, api_token.data as data, users.id as usersId, users.data as usersdata"; if (count) { @@ -112,13 +108,9 @@ app.get("/", async (req, res) => { } const query = parseFilters(fieldsMap, filters); query.push(sql`api_token.data->>'userId' = ${userId}`); - if (!req.user.admin) { - query.push( - sql`coalesce(api_token.data->>'projectId', ${ - req.user.defaultProjectId || "" - }) = ${req.project?.id || ""}` - ); - } + query.push( + sql`coalesce(api_token.data->>'projectId', '') = ${req.project?.id || ""}` + ); let fields = " api_token.id as id, api_token.data as data"; if (count) { diff --git a/packages/api/src/controllers/asset.test.ts b/packages/api/src/controllers/asset.test.ts index 32c86e5791..35fbd1e786 100644 --- a/packages/api/src/controllers/asset.test.ts +++ b/packages/api/src/controllers/asset.test.ts @@ -144,7 +144,7 @@ describe("controllers/asset", () => { type: "url", url: spec.url, }, - projectId: expect.any(String), //should have a default project id + projectId: "", //should be blank when using jwt and projectId not specified as query-param status: { phase: "waiting" }, }); @@ -191,7 +191,7 @@ describe("controllers/asset", () => { }); }); - it("should import asset (using jwt) for existing project (created with jwt) and list with filters", async () => { + it("should import asset (using jwt) for existing project (created with jwt)", async () => { const spec = { name: "test", url: "https://example.com/test.mp4", @@ -221,15 +221,6 @@ describe("controllers/asset", () => { res = await client.get(`/project/${projectId}`); expect(res.status).toBe(200); expect(await res.json()).toBeDefined(); //api-key be retrieve if adminApiKey is used.. - - res = await client.get( - `/asset?limit=10&allUsers=true&filters=[{"id":"playbackId","value":"${asset.playbackId}"}]` - ); - expect(res.status).toBe(200); - let assets = await res.json(); - expect(assets).toHaveLength(1); - expect(assets[0].projectId).toBe(projectId); - expect(assets[0].playbackId).toBe(asset.playbackId); }); it("should import asset (using api-token) for existing project (created with jwt)", async () => { @@ -263,25 +254,6 @@ describe("controllers/asset", () => { expect(project.id).toBeDefined(); }); - it("should import asset when projectId is not passed and list with projectId", async () => { - const spec = { - name: "test", - url: "https://example.com/test.mp4", - }; - let res = await client.post(`/asset/upload/url`, spec); - expect(res.status).toBe(201); - const { asset, task } = await res.json(); - - client.jwtAuth = null; - client.apiKey = adminApiKey; - - res = await client.get(`/asset?limit=10&allUsers=true`); - expect(res.status).toBe(200); - let assets = await res.json(); - expect(assets).toHaveLength(1); - expect(assets[0].projectId).toBe(asset.projectId); - }); - it("should NOT import asset (using api-key) when projectId passed as ouery-param", async () => { const spec = { name: "test", @@ -310,7 +282,7 @@ describe("controllers/asset", () => { type: "url", url: spec.url, }, - projectId: adminUser.defaultProjectId, + projectId: "", //should be blank when using an existing api-key and new project was created status: { phase: "waiting" }, }); }); diff --git a/packages/api/src/controllers/asset.ts b/packages/api/src/controllers/asset.ts index a65de98395..9427772034 100644 --- a/packages/api/src/controllers/asset.ts +++ b/packages/api/src/controllers/asset.ts @@ -22,7 +22,6 @@ import { reqUseReplica, isValidBase64, mapInputCreatorId, - addDefaultProjectId, } from "./helpers"; import { db } from "../store"; import sql from "sql-template-strings"; @@ -240,7 +239,7 @@ export async function validateAssetPayload( name: payload.name, source, staticMp4: payload.staticMp4, - projectId: req.project?.id, + projectId: req.project?.id ?? "", creatorId: mapInputCreatorId(payload.creatorId), playbackPolicy, objectStoreId: payload.objectStoreId || (await defaultObjectStoreId(req)), @@ -496,7 +495,6 @@ export async function createAsset(asset: WithID, queue: Queue) { timestamp: asset.createdAt, event: "asset.created", userId: asset.userId, - projectId: asset.projectId, payload: { asset: { id: asset.id, @@ -615,24 +613,14 @@ export async function toExternalAsset( return a; } -app.use(mung.jsonAsync(addDefaultProjectId)); - app.use( mung.jsonAsync(async function cleanWriteOnlyResponses( data: WithID[] | WithID | { asset: WithID }, req ) { const { details } = toStringValues(req.query); - const toExternalAssetFunc = async (a: Asset) => { - const modifiedAsset = await toExternalAsset( - a, - req.config, - !!details, - req.user.admin - ); - - return modifiedAsset; - }; + const toExternalAssetFunc = (a: Asset) => + toExternalAsset(a, req.config, !!details, req.user.admin); if (Array.isArray(data)) { return Promise.all(data.map(toExternalAssetFunc)); @@ -712,14 +700,9 @@ app.get("/", authorizer({}), async (req, res) => { query.push(sql`asset.data->>'deleted' IS NULL`); } - if (!req.user.admin) { - query.push( - sql`coalesce(asset.data->>'projectId', ${ - req.user.defaultProjectId || "" - }) = ${req.project?.id || ""}` - ); - } - + query.push( + sql`coalesce(asset.data->>'projectId', '') = ${req.project?.id || ""}` + ); if (req.user.admin && deleting === "true") { const deletionThreshold = new Date( Date.now() - DELETE_ASSET_DELAY @@ -792,7 +775,13 @@ app.get("/:id", authorizer({}), async (req, res) => { if (!asset || asset.deleted) { throw new NotFoundError(`Asset not found`); } - req.checkResourceAccess(asset); + + if (req.user.admin !== true && req.user.id !== asset.userId) { + throw new ForbiddenError( + "user can only request information on their own assets" + ); + } + res.json(asset); }); diff --git a/packages/api/src/controllers/auth.ts b/packages/api/src/controllers/auth.ts index ac8e9fac07..aabca9b911 100644 --- a/packages/api/src/controllers/auth.ts +++ b/packages/api/src/controllers/auth.ts @@ -69,7 +69,6 @@ app.all( : userIds.find((id) => !!id) || req.user.id; res.header("x-livepeer-user-id", userId); res.header("x-livepeer-is-caller-admin", req.user.admin ? "true" : "false"); - res.header("x-livepeer-project-id", req.project?.id || ""); res.status(204).end(); } diff --git a/packages/api/src/controllers/clip.ts b/packages/api/src/controllers/clip.ts index d9021b68d2..4361aed047 100644 --- a/packages/api/src/controllers/clip.ts +++ b/packages/api/src/controllers/clip.ts @@ -177,7 +177,6 @@ app.post( user: owner, token: req.token, config: req.config, - project: req.project, }, id, uPlaybackId, @@ -225,7 +224,6 @@ const fieldsMap = { createdAt: { val: `asset.data->'createdAt'`, type: "int" }, updatedAt: { val: `asset.data->'status'->'updatedAt'`, type: "int" }, userId: `asset.data->>'userId'`, - projectId: `asset.data->>'projectId'`, playbackId: `asset.data->>'playbackId'`, sourceType: `asset.data->'source'->>'type'`, sourceSessionId: `asset.data->'source'->>'sessionId'`, @@ -271,12 +269,6 @@ export const getClips = async ( let output: WithID[]; let newCursor: string; - if (!req.user.admin) { - query.push( - sql`coalesce(asset.data->>'projectId', '') = ${req.project?.id || ""}` - ); - } - query.push(sql`asset.data->>'userId' = ${req.user.id}`); if (!content) { diff --git a/packages/api/src/controllers/helpers.test.ts b/packages/api/src/controllers/helpers.test.ts index 622c22753e..b93c1fb6ce 100644 --- a/packages/api/src/controllers/helpers.test.ts +++ b/packages/api/src/controllers/helpers.test.ts @@ -4,13 +4,11 @@ import { ObjectStore, User } from "../schema/types"; import { db } from "../store"; import { v4 as uuid } from "uuid"; import { - addDefaultProjectId, deleteCredentials, getS3PresignedUrl, toObjectStoreUrl, toWeb3StorageUrl, } from "./helpers"; -import { Request, Response } from "express"; let server: TestServer; @@ -233,61 +231,4 @@ describe("convert w3 storage to object store URL", () => { "undefined property 'credentials.proof'" ); }); - - it("should append default project id", async () => { - const mockAsset = { - id: "asset1", - userId: "test", - user: { - id: "test", - admin: false, - defaultProjectId: "defaultProject1", - }, - projectId: undefined, - }; - const mockAsset2 = { - id: "asset2", - userId: "test", - projectId: undefined, - user: { - id: "test", - admin: false, - defaultProjectId: "defaultProject1", - }, - }; - - const assetList = [mockAsset, mockAsset2]; - - let mockReq: Request = { - user: { - id: "test", - admin: false, - defaultProjectId: "defaultProject1", - }, - project: { - id: "test", - }, - } as Request; - - let mockRes: Response = { - status: jest.fn().mockReturnThis(), - json: jest.fn(), - } as any as Response; - - // nonAdmin + singleAsset - let result = await addDefaultProjectId(mockAsset, mockReq, mockRes); - expect(result.projectId).toBe("defaultProject1"); - expect(assetList[0].projectId).toBe(undefined); - // nonAdmin + multipleAssets - result = await addDefaultProjectId(assetList, mockReq, mockRes); - expect(result[0].projectId).toBe("defaultProject1"); - mockReq.user.admin = true; - mockReq.user.defaultProjectId = "adminProjectId"; - // admin + singleAsset - result = await addDefaultProjectId(mockAsset, mockReq, mockRes); - expect(result.projectId).toBe("defaultProject1"); - // admin + multipleAssets - result = await addDefaultProjectId(assetList, mockReq, mockRes); - expect(result[0].projectId).toBe("defaultProject1"); - }); }); diff --git a/packages/api/src/controllers/helpers.ts b/packages/api/src/controllers/helpers.ts index 1eaee3899b..1f77d676bc 100644 --- a/packages/api/src/controllers/helpers.ts +++ b/packages/api/src/controllers/helpers.ts @@ -4,7 +4,7 @@ import { URL } from "url"; import fetch from "node-fetch"; import SendgridMail from "@sendgrid/mail"; import SendgridClient from "@sendgrid/client"; -import express, { Request, Response } from "express"; +import express, { Request } from "express"; import sql, { SQLStatement } from "sql-template-strings"; import { createHmac } from "crypto"; import { S3Client, PutObjectCommand, S3ClientConfig } from "@aws-sdk/client-s3"; @@ -16,8 +16,6 @@ import * as nativeCrypto from "crypto"; import { DBStream } from "../store/stream-table"; import { fetchWithTimeoutAndRedirects, sleep } from "../util"; import logger from "../logger"; -import { db } from "../store"; -import { v4 as uuid } from "uuid"; const ITERATIONS = 10000; const PAYMENT_FAILED_TIMEFRAME = 3 * 24 * 60 * 60 * 1000; @@ -736,75 +734,3 @@ export function mapInputCreatorId(inputId: InputCreatorId): CreatorId { ? { type: "unverified", value: inputId } : inputId; } - -export function getProjectId(req: Request): string { - let projectId = req.user.defaultProjectId ?? ""; - if (req.project?.id) { - projectId = req.project.id; - } - return projectId; -} - -export async function addDefaultProjectId( - body: any, - req: Request, - res: Response -) { - const deepClone = (obj) => { - return JSON.parse(JSON.stringify(obj)); - }; - - const enrichResponse = (document) => { - if ("id" in document && "userId" in document) { - if ( - (!document.projectId || document.projectId === "") && - req.user?.defaultProjectId - ) { - document.projectId = req.user.defaultProjectId; - } - } - }; - - const enrichResponseWithUserProjectId = (document) => { - if ("id" in document && "userId" in document && "user" in document) { - if ( - (!document.projectId || document.projectId === "") && - document.user?.defaultProjectId - ) { - document.projectId = document.user.defaultProjectId; - } - } - }; - - const clonedBody = deepClone(body); - - const processItem = (item) => { - if (typeof item === "object" && item !== null) { - if (req.user.admin) { - enrichResponseWithUserProjectId(item); - } else { - enrichResponse(item); - } - - Object.values(item).forEach((subItem) => { - if (typeof subItem === "object" && subItem !== null) { - if (req.user.admin) { - enrichResponseWithUserProjectId(subItem); - } else { - enrichResponse(subItem); - } - } - }); - } - }; - - if (Array.isArray(clonedBody)) { - clonedBody.forEach((item) => { - processItem(item); - }); - } else if (typeof clonedBody === "object" && clonedBody !== null) { - processItem(clonedBody); - } - - return clonedBody; -} diff --git a/packages/api/src/controllers/project.ts b/packages/api/src/controllers/project.ts index 9bdffb052b..54caecadb3 100644 --- a/packages/api/src/controllers/project.ts +++ b/packages/api/src/controllers/project.ts @@ -1,5 +1,5 @@ -import { Router } from "express"; -import { authorizer } from "../middleware"; +import { Request, Router } from "express"; +import { authorizer, validatePost } from "../middleware"; import { db } from "../store"; import { v4 as uuid } from "uuid"; import { @@ -8,7 +8,13 @@ import { parseOrder, toStringValues, } from "./helpers"; -import { NotFoundError, ForbiddenError } from "../store/errors"; +import { + NotFoundError, + ForbiddenError, + BadRequestError, + BadGatewayError, + InternalServerError, +} from "../store/errors"; import sql from "sql-template-strings"; import { WithID } from "../store/types"; import { Project } from "../schema/types"; @@ -133,46 +139,4 @@ app.post("/", authorizer({}), async (req, res) => { res.json(project); }); -app.patch("/:id", authorizer({}), async (req, res) => { - const project = await db.project.get(req.params.id, { - useReplica: false, - }); - - if (!project || project.deleted) { - throw new NotFoundError(`project not found`); - } - - if (req.user.admin !== true && req.user.id !== project.userId) { - throw new ForbiddenError("user can only update their own projects"); - } - - const { name } = req.body; - - await db.project.update(req.params.id, { - name: name, - }); - - res.status(204); - res.end(); -}); - -app.delete("/:id", authorizer({}), async (req, res) => { - const project = await db.project.get(req.params.id, { - useReplica: false, - }); - - if (!project || project.deleted) { - throw new NotFoundError(`project not found`); - } - - if (req.user.admin !== true && req.user.id !== project.userId) { - throw new ForbiddenError("user can only delete their own projects"); - } - - // Todo: Long running cronjob for cascade delete - - res.status(204); - res.end(); -}); - export default app; diff --git a/packages/api/src/controllers/session.ts b/packages/api/src/controllers/session.ts index 921ca78016..c82d2afc62 100644 --- a/packages/api/src/controllers/session.ts +++ b/packages/api/src/controllers/session.ts @@ -5,7 +5,7 @@ import { authorizer, hasAccessToResource } from "../middleware"; import { User, Project } from "../schema/types"; import { db } from "../store"; import { DBSession } from "../store/session-table"; -import { addDefaultProjectId, pathJoin } from "../controllers/helpers"; +import { pathJoin } from "../controllers/helpers"; import { fetchWithTimeout } from "../util"; import { DBStream } from "../store/stream-table"; import { WithID } from "../store/types"; @@ -25,7 +25,6 @@ import { import { LVPR_SDK_EMAILS, getClips } from "./clip"; import { NotFoundError } from "../store/errors"; import { cache } from "../store/cache"; -import mung from "express-mung"; const app = Router(); @@ -59,8 +58,6 @@ const fieldsMap: FieldsMap = { recordingStatus: `session.data->'recordingStatus'`, }; -app.use(mung.jsonAsync(addDefaultProjectId)); - app.get("/", authorizer({}), async (req, res, next) => { let { limit, cursor, all, order, filters, userId, parentId, count } = toStringValues(req.query); diff --git a/packages/api/src/controllers/signing-key.ts b/packages/api/src/controllers/signing-key.ts index e1e05768e5..07f7d953c5 100644 --- a/packages/api/src/controllers/signing-key.ts +++ b/packages/api/src/controllers/signing-key.ts @@ -1,9 +1,7 @@ import { authorizer, validatePost } from "../middleware"; -import { Request, Response, Router } from "express"; +import { Router } from "express"; import { FieldsMap, - addDefaultProjectId, - getProjectId, makeNextHREF, parseFilters, parseOrder, @@ -21,7 +19,6 @@ import { } from "../schema/types"; import { WithID } from "../store/types"; import { SignOptions, sign } from "jsonwebtoken"; -import mung from "express-mung"; const fieldsMap: FieldsMap = { id: `signing_key.ID`, @@ -58,8 +55,6 @@ async function generateSigningKeys() { const signingKeyApp = Router(); -signingKeyApp.use(mung.jsonAsync(addDefaultProjectId)); - signingKeyApp.get("/", authorizer({}), async (req, res) => { let { limit, cursor, all, allUsers, order, filters, count } = toStringValues( req.query @@ -77,6 +72,12 @@ signingKeyApp.get("/", authorizer({}), async (req, res) => { query.push(sql`signing_key.data->>'deleted' IS NULL`); } + query.push( + sql`coalesce(signing_key.data->>'projectId', '') = ${ + req.project?.id || "" + }` + ); + let fields = " signing_key.id as id, signing_key.data as data, users.id as usersId, users.data as usersdata"; if (count) { @@ -113,9 +114,7 @@ signingKeyApp.get("/", authorizer({}), async (req, res) => { query.push(sql`signing_key.data->>'deleted' IS NULL`); query.push( - sql`coalesce(signing_key.data->>'projectId', ${ - req.user.defaultProjectId || "" - }) = ${req.project?.id || ""}` + sql`coalesce(signing_key.data->>'projectId', '') = ${req.project?.id || ""}` ); let fields = " signing_key.id as id, signing_key.data as data"; @@ -185,14 +184,13 @@ signingKeyApp.post( let b64PublicKey = Buffer.from(keypair.publicKey).toString("base64"); let b64PrivateKey = Buffer.from(keypair.privateKey).toString("base64"); - const projectId = getProjectId(req); var doc: WithID = { id, name: req.body.name || "Signing Key " + (output.length + 1), userId: req.user.id, createdAt: Date.now(), publicKey: b64PublicKey, - projectId, + projectId: req.project?.id ?? "", }; await db.signingKey.create(doc); diff --git a/packages/api/src/controllers/stream.test.ts b/packages/api/src/controllers/stream.test.ts index 5a90e78fda..4dc5fc8b82 100644 --- a/packages/api/src/controllers/stream.test.ts +++ b/packages/api/src/controllers/stream.test.ts @@ -21,7 +21,6 @@ import { startAuxTestServer, createProject, createApiToken, - useApiTokenWithProject, } from "../test-helpers"; import serverPromise, { TestServer } from "../test-server"; import { semaphore, sleep } from "../util"; @@ -1216,7 +1215,6 @@ describe("controllers/stream", () => { it("should disallow patching other users streams", async () => { client.jwtAuth = nonAdminToken; - client.apiKey = ""; const res = await client.patch(patchPath, {}); expect(res.status).toBe(404); }); @@ -1384,7 +1382,7 @@ describe("controllers/stream", () => { expect(res.status).toBe(200); const streams = await res.json(); expect(streams.length).toEqual(3); - expect(streams[0].id).toEqual(source[3].id); + expect(streams[0]).toEqual(source[3]); expect(streams[0].userId).toEqual(nonAdminUser.id); expect(res.headers.raw().link).toBeDefined(); expect(res.headers.raw().link.length).toBe(1); @@ -1394,7 +1392,7 @@ describe("controllers/stream", () => { expect(nextRes.status).toBe(200); const nextStreams = await nextRes.json(); expect(nextStreams.length).toEqual(1); - expect(nextStreams[0].id).toEqual(source[6].id); + expect(nextStreams[0]).toEqual(source[6]); expect(nextStreams[0].userId).toEqual(nonAdminUser.id); }); @@ -1900,11 +1898,7 @@ describe("controllers/stream", () => { id: expect.stringMatching(uuidRegex), webhookId: webhookObj.id, event: "stream.detection", - stream: { - ...stream, - streamKey: undefined, - projectId: expect.any(String), - }, + stream: { ...stream, streamKey: undefined }, payload: { sceneClassification, seqNo: 1 }, }); }); diff --git a/packages/api/src/controllers/stream.ts b/packages/api/src/controllers/stream.ts index eb59c293e9..138005fc9a 100644 --- a/packages/api/src/controllers/stream.ts +++ b/packages/api/src/controllers/stream.ts @@ -5,7 +5,6 @@ import sql from "sql-template-strings"; import { parse as parseUrl } from "url"; import { v4 as uuid } from "uuid"; -import mung from "express-mung"; import logger from "../logger"; import { authorizer, @@ -49,7 +48,6 @@ import { } from "./generate-keys"; import { FieldsMap, - addDefaultProjectId, makeNextHREF, mapInputCreatorId, parseFilters, @@ -121,8 +119,6 @@ const hackMistSettings = (req: Request, profiles: Profile[]): Profile[] => { }); }; -app.use(mung.jsonAsync(addDefaultProjectId)); - async function validateMultistreamTarget( userId: string, profileNames: Set, @@ -269,7 +265,6 @@ async function triggerManyIdleStreamsWebhook(ids: string[], queue: Queue) { timestamp: Date.now(), event: "stream.idle", streamId: stream.id, - projectId: stream.projectId, userId: user.id, }); }) @@ -502,9 +497,7 @@ app.get("/", authorizer({}), async (req, res) => { if (!req.user.admin) { userId = req.user.id; query.push( - sql`coalesce(stream.data->>'projectId', ${ - req.user.defaultProjectId || "" - }) = ${req.project?.id || ""}` + sql`coalesce(stream.data->>'projectId', '') = ${req.project?.id || ""}` ); } if (!all || all === "false" || !req.user.admin) { @@ -768,6 +761,8 @@ app.get("/user/:userId", authorizer({}), async (req, res) => { const { userId } = req.params; let { limit, cursor, streamsonly, sessionsonly } = toStringValues(req.query); + let projectId = req.token?.projectId; + if (req.user.admin !== true && req.user.id !== req.params.userId) { res.status(403); return res.json({ @@ -777,14 +772,8 @@ app.get("/user/:userId", authorizer({}), async (req, res) => { const query = [ sql`data->>'deleted' IS NULL`, sql`data->>'userId' = ${userId}`, + sql`coalesce(data->>'projectId', '') = ${projectId || ""}`, ]; - if (!req.user.admin) { - query.push( - sql`coalesce(data->>'projectId', ${req.user.defaultProjectId || ""}) = ${ - req.project?.id || "" - }` - ); - } if (streamsonly) { query.push(sql`data->>'parentId' IS NULL`); } else if (sessionsonly) { @@ -1174,9 +1163,7 @@ app.put( [ sql`data->>'userId' = ${req.user.id}`, sql`data->>'deleted' IS NULL`, - sql`coalesce(data->>'projectId', ${ - req.user.defaultProjectId || "" - }) = ${req.project?.id || ""}`, + sql`coalesce(data->>'projectId', '') = ${req.project?.id || ""}`, ...filters, ], { useReplica: false } @@ -1348,9 +1335,7 @@ app.post( sql`data->>'userId' = ${req.user.id}`, sql`data->>'deleted' IS NULL`, sql`data->'pull'->>'source' = ${payload.pull.source}`, - sql`coalesce(data->>'projectId', ${ - req.user.defaultProjectId || "" - }) = ${req.project?.id || ""}`, + sql`coalesce(data->>'projectId', '') = ${req.project?.id || ""}`, ], { useReplica: false } ); @@ -1424,7 +1409,7 @@ async function handleCreateStream(req: Request, payload: NewStreamPayload) { renditions: {}, objectStoreId, id, - projectId: req.project?.id, + projectId: req.project?.id ?? "", createdAt, streamKey, playbackId, @@ -1596,7 +1581,6 @@ async function setStreamActiveWithHooks( streamId: stream.id, event: event, userId: stream.userId, - projectId: stream.projectId, }) .catch((err) => { logger.error( @@ -1743,7 +1727,6 @@ async function publishRecordingStartedHook( timestamp: Date.now(), streamId: session.parentId, userId: session.userId, - projectId: session.projectId, event: "recording.started", payload: { session: await toExternalSession(config, session, ingest) }, }); @@ -1767,7 +1750,6 @@ async function publishDelayedRecordingWaitingHook( streamId: session.parentId, event: "recording.waiting", userId: session.userId, - projectId: session.projectId, sessionId: session.id, payload: { session: { @@ -2441,7 +2423,6 @@ app.post( streamId: stream.id, event: "stream.detection", userId: stream.userId, - projectId: stream.projectId, payload: { seqNo, sceneClassification, diff --git a/packages/api/src/controllers/user.ts b/packages/api/src/controllers/user.ts index e6523161b1..356ff89003 100644 --- a/packages/api/src/controllers/user.ts +++ b/packages/api/src/controllers/user.ts @@ -397,13 +397,6 @@ app.post("/", validatePost("user"), async (req, res) => { }; } - let project = await db.project.create({ - id: uuid(), - name: "My default project", - userId: id, - createdAt: Date.now(), - }); - await db.user.create({ kind: "user", id: id, @@ -418,7 +411,6 @@ app.post("/", validatePost("user"), async (req, res) => { lastName, organization, phone, - defaultProjectId: project.id, ...stripeFields, }); @@ -794,21 +786,6 @@ app.post("/token", validatePost("user"), async (req, res) => { ], }); } - - if (!user.defaultProjectId) { - const id = uuid(); - await db.project.create({ - id: id, - name: "My default project", - userId: user.id, - createdAt: Date.now(), - }); - - await db.user.update(user.id, { - defaultProjectId: id, - }); - } - res.status(201); res.json({ id: user.id, email: user.email, token, refreshToken }); }); @@ -1501,53 +1478,4 @@ app.post( } ); -// Utility to migrate users to defaultProjects -// To call once and then we can remove it -app.post( - "/migrate/userDefaultProject", - authorizer({ anyAdmin: true }), - async (req, res) => { - // parse limit from querystring - const limit = parseInt(req.query.limit?.toString() || "100"); - - const [users] = await db.user.find( - [ - sql`(users.data->>'defaultProjectId' = '' OR users.data->>'defaultProjectId' IS NULL)`, - sql`(users.data->>'admin' = 'false' OR users.data->>'admin' IS NULL)`, - ], - { limit } - ); - - const results = []; - - for (const user of users) { - const project = await db.project.create({ - id: uuid(), - name: "My default project", - userId: user.id, - createdAt: Date.now(), - }); - - const defaultProjectId = project.id; - await db.user.update(user.id, { - defaultProjectId, - }); - - results.push({ - id: user.id, - defaultProjectId, - }); - } - - res.status(200).json({ - migrated: results.length, - total: users.length, - users: results.map(({ id, defaultProjectId }) => ({ - id, - defaultProjectId, - })), - }); - } -); - export default app; diff --git a/packages/api/src/controllers/webhook.test.ts b/packages/api/src/controllers/webhook.test.ts index 7cb0235abb..e08f534c4d 100644 --- a/packages/api/src/controllers/webhook.test.ts +++ b/packages/api/src/controllers/webhook.test.ts @@ -9,7 +9,6 @@ let mockAdminUser; let mockNonAdminUser; let postMockStream; let mockWebhook; -let projectId; // jest.setTimeout(70000) beforeAll(async () => { @@ -274,17 +273,16 @@ describe("controllers/webhook", () => { // create a stream object const now = Date.now(); - postMockStream.name = "eli_is_very_cool"; // :D + postMockStream.name = "eli_is_cool"; // :D const res = await client.post("/stream", { ...postMockStream }); expect(res.status).toBe(201); const stream = await res.json(); expect(stream.id).toBeDefined(); expect(stream.kind).toBe("stream"); - expect(stream.name).toBe("eli_is_very_cool"); + expect(stream.name).toBe("eli_is_cool"); expect(stream.createdAt).toBeGreaterThanOrEqual(now); - const document = await client.get(`/stream/${stream.id}`); - const gotStream = await document.json(); - expect(server.db.stream.addDefaultFields(gotStream)).toEqual(stream); + const document = await server.store.get(`stream/${stream.id}`); + expect(server.db.stream.addDefaultFields(document)).toEqual(stream); // trigger const setActiveRes = await client.put(`/stream/${stream.id}/setactive`, { @@ -357,7 +355,8 @@ describe("controllers/webhook", () => { ); expect(getRes.status).toBe(200); const getJson = await getRes.json(); - expect(getJson.id).toEqual(webhookRequest.id); + expect(getJson).toEqual(webhookRequest); + const resendRes = await client.post( `/webhook/${generatedWebhook.id}/log/${webhookRequest.id}/resend` ); diff --git a/packages/api/src/controllers/webhook.ts b/packages/api/src/controllers/webhook.ts index 4d15231185..bb3e317783 100644 --- a/packages/api/src/controllers/webhook.ts +++ b/packages/api/src/controllers/webhook.ts @@ -4,19 +4,11 @@ import { validatePost } from "../middleware"; import Router from "express/lib/router"; import logger from "../logger"; import { v4 as uuid } from "uuid"; -import { - makeNextHREF, - parseFilters, - parseOrder, - FieldsMap, - addDefaultProjectId, - getProjectId, -} from "./helpers"; +import { makeNextHREF, parseFilters, parseOrder, FieldsMap } from "./helpers"; import { db } from "../store"; import sql from "sql-template-strings"; import { UnprocessableEntityError, NotFoundError } from "../store/errors"; import webhookLog from "./webhook-log"; -import mung from "express-mung"; function validateWebhookPayload(id, userId, projectId, createdAt, payload) { try { @@ -35,7 +27,7 @@ function validateWebhookPayload(id, userId, projectId, createdAt, payload) { return { id, userId, - projectId, + projectId: projectId ?? "", createdAt, kind: "webhook", name: payload.name, @@ -48,8 +40,6 @@ function validateWebhookPayload(id, userId, projectId, createdAt, payload) { const app = Router(); -app.use(mung.jsonAsync(addDefaultProjectId)); - app.use("/:id/log", webhookLog); const fieldsMap: FieldsMap = { @@ -77,6 +67,9 @@ app.get("/", authorizer({}), async (req, res) => { if (!all || all === "false") { query.push(sql`webhook.data->>'deleted' IS NULL`); } + query.push( + sql`coalesce(webhook.data->>'projectId', '') = ${req.project?.id || ""}` + ); let fields = " webhook.id as id, webhook.data as data, users.id as usersId, users.data as usersdata"; @@ -109,9 +102,7 @@ app.get("/", authorizer({}), async (req, res) => { const query = parseFilters(fieldsMap, filters); query.push(sql`webhook.data->>'userId' = ${req.user.id}`); query.push( - sql`coalesce(webhook.data->>'projectId', ${ - req.user.defaultProjectId || "" - }) = ${req.project?.id || ""}` + sql`coalesce(webhook.data->>'projectId', '') = ${req.project?.id || ""}` ); if (!all || all === "false" || !req.user.admin) { diff --git a/packages/api/src/middleware/auth.ts b/packages/api/src/middleware/auth.ts index fccb218bcb..19ef0b958d 100644 --- a/packages/api/src/middleware/auth.ts +++ b/packages/api/src/middleware/auth.ts @@ -84,8 +84,7 @@ export function hasAccessToResource( isAdmin || (!resource.deleted && resource.userId === user.id && - (resource.projectId ?? user.defaultProjectId ?? "") === - (project?.id ?? "")) + (resource.projectId ?? "") === (project?.id ?? "")) ); } @@ -190,10 +189,6 @@ function authenticator(): RequestHandler { req.token = tokenObject; req.user = user; - if (!projectId) { - projectId = req.user.defaultProjectId; - } - if (projectId) { project = await getProject(req, projectId); req.project = project; diff --git a/packages/api/src/schema/db-schema.yaml b/packages/api/src/schema/db-schema.yaml index 9582b3dd2f..e08fe5331d 100644 --- a/packages/api/src/schema/db-schema.yaml +++ b/packages/api/src/schema/db-schema.yaml @@ -1194,9 +1194,6 @@ components: directPlayback: type: boolean default: false - defaultProjectId: - type: string - example: 66E2161C-7670-4D05-B71D-DA2D6979556F kind: type: string readOnly: true diff --git a/packages/api/src/store/messages.ts b/packages/api/src/store/messages.ts index 94dea27016..0ce057530a 100644 --- a/packages/api/src/store/messages.ts +++ b/packages/api/src/store/messages.ts @@ -32,7 +32,6 @@ namespace messages { event: EventKey; userId: string; sessionId?: string; - projectId?: string; // Additional information about the event to be sent to the user on the // webhook request. payload?: TPayload; diff --git a/packages/api/src/task/scheduler.ts b/packages/api/src/task/scheduler.ts index d3cee61a9b..59d708e637 100644 --- a/packages/api/src/task/scheduler.ts +++ b/packages/api/src/task/scheduler.ts @@ -258,7 +258,6 @@ export class TaskScheduler { streamId: session.parentId, event: "recording.ready", userId: session.userId, - projectId: session.projectId, sessionId: session.id, payload: { recordingUrl: externalSession?.recordingUrl, @@ -624,7 +623,6 @@ export class TaskScheduler { timestamp, event, userId: asset.userId, - projectId: asset.projectId, payload: { asset: { id: asset.id, @@ -643,7 +641,6 @@ export class TaskScheduler { timestamp, event: assetEvent, userId: asset.userId, - projectId: asset.projectId, payload: { id: asset.id, snapshot, diff --git a/packages/api/src/test-helpers.ts b/packages/api/src/test-helpers.ts index 6ef9b70c03..b35d4e48b7 100644 --- a/packages/api/src/test-helpers.ts +++ b/packages/api/src/test-helpers.ts @@ -206,21 +206,6 @@ export async function createProject(client: TestClient) { return project; } -export async function useApiTokenWithProject( - client: TestClient, - token: string -) { - client.jwtAuth = token; - const project = await createProject(client); - const newApiKey = await createApiToken({ - client: client, - projectId: project.id, - jwtAuthToken: token, - }); - client.apiKey = newApiKey.id; - return client; -} - export async function createApiToken({ client, projectId, diff --git a/packages/api/src/webhooks/cannon.ts b/packages/api/src/webhooks/cannon.ts index ace8673055..e9869fc47a 100644 --- a/packages/api/src/webhooks/cannon.ts +++ b/packages/api/src/webhooks/cannon.ts @@ -564,7 +564,6 @@ export default class WebhookCannon { id, playbackId, userId: session.userId, - projectId: session.projectId, createdAt: session.createdAt, source: { type: "recording", sessionId: session.id }, status: { phase: "waiting", updatedAt: Date.now() },