hasura / graphql-engine

Blazing fast, instant realtime GraphQL APIs on your DB with fine grained access control, also trigger webhooks on database events.
https://hasura.io
Apache License 2.0
31.14k stars 2.77k forks source link

Override "select filter permissions" from original tables with custom functions #8328

Open avallete opened 2 years ago

avallete commented 2 years ago

Is your proposal related to a problem?

On our app, we have a global "projects" table, where the permission are the following simple one:

This table has a bunch of relations to other tables, some with the same access conditions, some with a more restricted one.

However, in some cases where someone, administrator of an org, want to see an overview dashboard of all the projects his org members are in, we would want to "bypass" the projects filter permissions, and apply a heavier to compute access permission logic, but still allowing the API to work with "projects". Allowing the user to access "basics fields" on it, and making the "relations with details" still protected (since for instance a document on a project check if your a project member, the administrator will be able to see the "name" of a project (directly on the project table) but will have an empty list of "documents" for it, since he's not a member of this project).

Digging into the repo I found this: https://github.com/hasura/graphql-engine/blob/71ae144aa623c156064ffe21eb38020ccf315a7a/rfcs/function-permissions.md#L77

So it seems that the solution has been already reflected upon and for the part which is:

1. Ability to override the `filter` of the select permission defined on
   functions returning table types. <TODO: motivate the use case>

I want to put this case in:

Describe the solution you'd like

Something like this in the metadata:

function:
  name: get_user_project_org_overview
  schema: public
configuration:
  custom_root_fields: {}
  session_argument: hasura_session
permissions:
- role: user
  description:
      select_permissions:
          - permission:
              filter: {} 

This would override the "original" select permissions for the user role and apply the new ones (which in this cases are empty) to the select query. Allowing the "get_user_project_org_overview" to handle permissions access and still re-use the common type of "project" in terms of graphql API.

Describe alternatives you've considered

As for now, I don't see anything to achieve what I want except:

  1. Create a view like so CREATE VIEW v_projects_overview AS SELECT * FROM projects;
  2. Re-apply the relations/permissions rules on the newly created view.

This come with 2 major downside:

  1. A "view" polute the database for no other purpose than "recreating a type" for the graphql api.
  2. No possibilities to re-use the project type, meaning that if you want to add a new field on it, you must think about updating your custom function as well

If the feature is approved, would you be willing to submit a PR?

Never done haskell, probably not the best idea πŸ˜‚

Aarbel commented 1 year ago

up

osdiab commented 1 year ago

I have a use case for this - I have a custom mutation that is much more efficient if it's done entirely in SQL directly. Basically just dumps a set of data from one table to another table. If I want to do this with the GQL API now, we need to fetch all the data from the database and then write all the same data back to it. Not only is that really inefficient, but on large inputs, it also crashes the Hasura auth webhook since we use a POST request and evidently Hasura detonates once the forwarded body gets too long (we need to move to JWT auth πŸ˜… ).

I am having this return the data that was inserted, in part because that's convenient, but in part because I don't see any way to add a custom mutation otherwise; I'd be fine to make it just RETURNS void or the affected rows or something, but if I do that then I can't have the GQL engine track it. So maybe I'm missing something, but this is how I found I could create a mutation that runs custom SQL.

So we have this custom function, but it's using SELECT permissions on the output table rather than what the endpoint is actually for - INSERTing into that table. So it's just using the wrong set of permissions altogether. If we did it with the GQL API, it would work fine since it would apply the insert permissions normally. But I don't think I can achieve what we're trying to do with Hasura in this case, because regardless if we are enabling or disabling HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS, it will always use the SELECT permissions on the target table erroneously.

So our workaround for now is to pass along the hasura_session argument, use that to write a modified_by column, and then write our own AFTER INSERT constraint trigger to do the permissions checks we ordinarily would define in Hasura (similar to what we ended up doing to work around #9299 ). A good amount of running around. (also HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS being enabled by default seems like a nice footgun to open a security hole in your app, but that's a separate discussion...).

cc @0x777 @coco98 given that the linked RFC above needed some use cases for this - not sure if there's an alternative I'm missing but here's one.