Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Projects in Studio #2078

Merged
merged 5 commits into from
Mar 11, 2024
Merged

Projects in Studio #2078

merged 5 commits into from
Mar 11, 2024

Conversation

emranemran
Copy link
Contributor

@emranemran emranemran commented Feb 27, 2024

Initial set of changes from myself + @mjh1 to enable "Project" environments in Studio:

  • Added boiler plate schema changes for project tables
  • Added project.ts to handle creation/retrieval of projects
  • Updated asset.ts to use projectId logic where applicable

A few notes:

  • I'm would like to merge changes as we go so that it's not one giant PR at the end that's difficult to review -- so we're making sure that projectId is not enforced anywhere right now so that we don't break backwards compatibility. Any records with an empty or missing projectId will get categorized as default project in the dashboard.
  • I'm opening up this PR early to collect feedback in case we're going down the wrong path. Please review the changes and suggest anything that might have been missed.
  • TODO: fix/add tests...

PS: this is my first large typrscript PR so be gentle :)

@emranemran emranemran requested a review from a team as a code owner February 27, 2024 01:19
Copy link

vercel bot commented Feb 27, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
livepeer-studio ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 7, 2024 9:53pm

packages/api/src/controllers/project.ts Dismissed Show dismissed Hide dismissed
packages/api/src/controllers/project.ts Fixed Show fixed Hide fixed
packages/api/src/controllers/project.ts Dismissed Show dismissed Hide dismissed
Copy link
Contributor

@leszko leszko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emranemran @mjh1 kudos for sending the PR early.

I didn't manage to review it all, but I added some first comments. PTAL.

@@ -200,6 +202,13 @@ export async function validateAssetPayload(
}
}

if (payload.projectId) {
const project = await getProject(req);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we shouldn't add project to req in

function authenticator(): RequestHandler {

My thinking is that now, authenticator() assigns user to each request. But from now on, the request (api token) will be associated always with one project, so maybe we should inject it in authenticator() and then there would be no need to call getProject() everywhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@victorges and I discussed something like that but Victor suggested we need to maintain compatibility with the SDK I believe. I'm still a bit on the fence on this b/c from the dashboard perspective, project is basically a "filter" to categorize the assets, streams, etc. But maybe philosophically what you're saying makes sense. wdyt @victorges?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can still maintain compatibility but having the projectId added to the request as Rafal suggests will just neaten the code up a bit. User and project would both exist on the request object then, and we can maintain compatibility by checking both in the controllers.

Copy link
Contributor

@mjh1 mjh1 Feb 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other thing is we don't want projectId to be part of the payload, we should be taking it from the api key being used. We could potentially leave that for a later PR though and leave this one mainly as just adding the projects controller.
Edit: I'm working on this bit locally atm, including the authenticator() rafal mentioned (branched off this), let me know if you want to pair etc WIP: #2081

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mjh1: I saw your edit comment too late and started making simliar changes to my branch - but I scrapped those and just cherry-picked yours here instead. Could we use this branch to pair for integration? I added another commit to make some edits but currently a bit broken when trying to create new api-keys -- see my comment here: #2078 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guys, I think you're on the right track!

Wrt what @mjh1 wrote, I think that we should not send projectId in the payload, because projectId is derived from API TOKEN. The same way, we don't send userId in every request.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok cool yep let's just use this branch

@@ -654,6 +674,14 @@ app.get("/", authorizer({}), async (req, res) => {
query.push(sql`asset.data->>'deleted' IS NULL`);
}

if (projectId) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we filter by projectId, do we also need to filter by userId in the line 683?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, don't we? We're trying to get all assets beloniging to a specific userId broken down by a specific projectId.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't one project belong to one user only? Then, filtering by projectId should be enough.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think projectId should be its own queryparam like this, we can add it to the fieldsMap instead. I also think we should treat it similar to userId in that only admins are allowed to use it. For non-admins we should just take the projectId associated with the api key being used.

if (count) {
fields = fields + ", count(*) OVER() AS count";
}
const from = `project left join users on project.data->>'userId' = users.id`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it would be possible to put the SQL queries inside the table? I know it's the same in other controllers, but I wonder if it would make sense to abstract it and put inside the store/*-table.ts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do that - I just followed the existing patterns in other files.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, that's probably fine, we can try to clean it up for all controllers in some other PR.

Copy link
Contributor

@leszko leszko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good from my end. You can decide with @victorges on the open discussions.

Plus, please add unit tests.

@emranemran
Copy link
Contributor Author

@mjh1 @leszko: a few questions:

  • Let's assume a project exists in the dashboard. Now the user creates an api-key which does a POST. Now looking at authorizer, Bearer method is not supported for creation of new api-keys. Tried sending a jwt request in box (using params in config/full-stack.json) but it fails when trying to retrieve a user object. Any ideas why?
  • For the migration case when a current user logs in, the authorizer will get an empty or missing projectId field via getRequest for every GET/POST/etc. Should a default project be created on first login after we migrate?

project = await db.project.get(projectId);
if (!project || project.deleted) {
return {};
//throw new NotFoundError(`project not found`);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure yet how to handle this case yet....just returning empty object for now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why doesn't the throwing of NotFoundError work?

@leszko
Copy link
Contributor

leszko commented Feb 29, 2024

  • Let's assume a project exists in the dashboard. Now the user creates an api-key which does a POST. Now looking at authorizer, Bearer method is not supported for creation of new api-keys. Tried sending a jwt request in box (using params in config/full-stack.json) but it fails when trying to retrieve a user object. Any ideas why?

Yeah, I think it make sense, that Bearer is not supported for creating api tokens, because you always need to first create them from the livepeer dashboard (read: jwt). Why doesn't it work in the box? No idea, unfortunately 😞

  • For the migration case when a current user logs in, the authorizer will get an empty or missing projectId field via getRequest for every GET/POST/etc. Should a default project be created on first login after we migrate?

This is something we started to discuss at some point. And I think we still have these 3 options:

  • Option 1: Add projectId for all users with the DB migration (then you don't have the case you described)
  • Option 2: Tweak it in the authorizer(), so in the authorizer, if a user does not have a project, then return some default project
  • Option 3: What you described, so create a project on-a-fly

I think that we discussed that the Option 3 probably makes the least sense. But you know better the code now, so hard to suggest what's the best.

@@ -26,7 +27,7 @@ export const EMAIL_VERIFICATION_CUTOFF_DATE = 1695765600000; // 2023-09-26T22:00
* are set to expire after a short time and the client manages using a refresh
* token for keeping the user logged in.
*/
export const NEVER_EXPIRING_JWT_CUTOFF_DATE = 1709251200000; // 2024-03-01T00:00:00.000Z
export const NEVER_EXPIRING_JWT_CUTOFF_DATE = 1740820816000; // 2024-03-01T00:00:00.000Z
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: just a HACK for now since I wasn't sure why we have this cutoff date.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK you don't need to change it. It was only a cutoff for the "old" JWT mechanism, and we're using now the "new" JWT mechanism. But Victor knows best since he updated "old" => "new" :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this doesn't need to change! Here's the doc on how the migration works: https://www.notion.so/livepeer/Studio-dashboard-authentication-310a74802de54f538e0ecb633f94ee8e?pvs=4

Copy link
Member

@victorges victorges left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

preliminary LGTM! Still missing projectId on a ton of resources and checks on the APIs

packages/api/src/controllers/api-token.js Outdated Show resolved Hide resolved
packages/api/src/controllers/asset.ts Show resolved Hide resolved
packages/api/src/controllers/project.ts Outdated Show resolved Hide resolved
project = await db.project.get(projectId);
if (!project || project.deleted) {
return {};
//throw new NotFoundError(`project not found`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why doesn't the throwing of NotFoundError work?

packages/api/src/controllers/project.ts Outdated Show resolved Hide resolved
packages/api/src/controllers/project.ts Outdated Show resolved Hide resolved
packages/api/src/middleware/auth.ts Outdated Show resolved Hide resolved
@@ -149,6 +155,9 @@ function authenticator(): RequestHandler {
req.token = tokenObject;
req.user = user;

project = await getProject(req, projectId);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably deserves some caching like user and api key above

@@ -149,6 +155,9 @@ function authenticator(): RequestHandler {
req.token = tokenObject;
req.user = user;

project = await getProject(req, projectId);
req.project = project;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add this project field to our Request type extensions here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah I meant that. Not sure how i missed it

@@ -1153,6 +1172,10 @@ components:
Name of the asset. This is not necessarily the filename, can be a
custom name or title
example: filename.mp4
projectId:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we still miss a bunch of these. They are probably readOnly too 🤔

@emranemran
Copy link
Contributor Author

emranemran commented Mar 4, 2024

preliminary LGTM! Still missing projectId on a ton of resources and checks on the APIs

So the plan with this PR is to get it ready and merged w/o breaking existing dashboard usage so that @suhailkakar can get started on frontend work. Then follow-up PRs will be used for streams/webhooks/etc and update those code paths for projectId handling.

return res.json(output);
});

app.get("/:id", authorizer({}), async (req, res) => {

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
Copy link
Contributor

@leszko leszko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

res.status(403);
return res.json({ errors: ["project not found"] });
}
console.log("YYY here:", project, req.params.id);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leftover?

const project = await db.project.get(req.params.id, {
useReplica: false,
});
console.log("YYY :", project, req.params.id);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leftover?

"user can only request information on their own projects"
);
}
console.log("YYY here here:", project, req.params.id);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leftover?

@emranemran emranemran merged commit 6c20637 into master Mar 11, 2024
12 of 13 checks passed
@emranemran emranemran deleted the emran/projects-project-assets branch March 11, 2024 00:54
mjh1 added a commit that referenced this pull request Mar 11, 2024
mjh1 added a commit that referenced this pull request Mar 11, 2024
emranemran added a commit that referenced this pull request Mar 13, 2024
emranemran added a commit that referenced this pull request Mar 25, 2024
…ollers (#2102)

* Revert "Revert "Projects in Studio (#2078)" (#2096)"

This reverts commit df62e49.

* asset/api-token: update sql logic to correctly select projectId

The previous queries were selecting records where projectId field in the
JSONB data column was missing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants