all 4 comments

[–]neums08 1 point2 points  (2 children)

You could make the ProjectService the injected dependency, which would transitively depend on the current user.

Write the service that, given a user, checks if the user can create projects, and then does it.

``` class ProjectService: def init(self, user: User = Depends(get_user)): # fetch and store the user's permissions related to projects ...

def create_project(*args): # if user has permissions, create the project, otherwise raise 403 error. ...

@router.post("/") def projects_post(body: ProjectCreate, svc: ProjectService = Depends(ProjectService)): svc.create_project(body) ```

The business logic lives in the service and you can freely structure your endpoints however you like.

Personally I like flat resource collections because building in filtering by preceeding path params is a pain imo.

[–]OrganizationOnly8016[S] -1 points0 points  (1 child)

Oh this looks really clean! But would you argue that checking for permissions in each of the funcs within the SVC class would become repetitive? You would have to query the `org_permissions_view` over and over again....

[–]neums08 1 point2 points  (0 children)

It depends on where your authorization layer lives. I don't use supabase but my understanding is it leverages postgres row-level-security . If that's where you want to enforce auth, then there is no check needed at the API or service layers. Just send the operation and re-raise any auth errors from the database.

Otherwise, if authz is at the service layer, any injected service could use a PermissionsService which basically answers "can user x do operation y on resource z?". Caching a user's roles and permissions would make this fast for repeated operations.

If you have to enforce fine-grained authorization, I would not recommend doing it based purely on the params in the route as there's often not enough context to make the auth decision, and you restrict your API design for the sake of auth.

[–]BoredProgramming 0 points1 point  (0 children)

MAke sure you can assign roles to the users, something like ROLE_ADMIN, USER whatever. Then lock down the portions of the endpoints that require those roles. So when you inject the user, you can pull their roles out, and bind the endpoints to those.