graphene-sqlalchemy icon indicating copy to clipboard operation
graphene-sqlalchemy copied to clipboard

Question: How do you recommend enforcing authorization?

Open hoffrocket opened this issue 6 years ago • 5 comments

Hello,

I'd like to systematically enforce authorization for nodes and individual fields within the nodes.

Conceptually something like this might work:

class MyNode(AuthZSQLAlchemyObjectType):
    class Meta:
        model = MyModel
        authorize_node_function = node_authorizer
        field_auth = dict(
              "name": all_authorizer,
              "private_things": self_only_authorizer,
       )

node_authorizer(model_instance) would get called whenever a new Node of that type is created. Only fields in the field_auth dict would be exposed in node, and then the associated function would be called like resolve_authorizer(model_instance, field_name)

Any opinions on the best way to achieve this?

hoffrocket avatar Mar 22 '19 22:03 hoffrocket

Hi @hoffrocket !

This is a "hot" subject in the graphene community (here for example) and no "official" answer have been defined yet, so the framework is not opiniated for now. I suggest you to follow the official graphql suggestion to implement the authorization logic on the business layer.

And for a more concrete answer, I have a suggestion from Dan Palmer for you :

We have a fairly lightweight solution for this:

  • We define get_queryset on our base ORM+graphene type.
  • We require “filter_to_user” and “filter_to_session” methods on those types (they fail at import time otherwise)
  • get_queryset funnels through those methods
  • the root node resolver is patched to use get_queryset.

This gets us basic authorisation and it’s fairly straightforward to implement. It also ties in nicely with our query optimisation and some other performance stuff.

Nabellaleen avatar Mar 27 '19 15:03 Nabellaleen

Thanks @Nabellaleen Those approaches seem inline with what we'd like to do.

Our goals:

  1. prevent new columns added to a SQLAlchemy model from leaking into the GraphQL schema (by enforcing use of only_fields like whitelist)
  2. associate a role or auth function with each column to enforce authorization rules at query time
  3. associate a role or auth function with the Node itself to catch things like id and custom fields that are not in the list of columns

We had been solving these concerns by being using only_fields on every Node and then overriding the resolve methods for columns where we need authorization controls. But that has become cumbersome and error prone. We have a lot of boilerplate code that looks like this:

class OurNode(SQLAlchemyObjectType):
    class Meta:
       only_fields = ("name", "created_at",...)
    
    @self_or_staff_required
    def resolve_name(self, info):
        return self.name

    @staff_required
    def resolve_created_at(self, info):
        return self.created_at

I've been experimenting with the concept above and it's pretty similar to what @dfee suggests here https://github.com/graphql-python/graphene-django/issues/79#issuecomment-306583068

It seems to work, and I wonder if we can get some consensus around an approach that could be pushed upstream and be generally useful.

One fundamental challenge to overcome in this community is that graphene-django is basically a superset and fork of graphene-sqlalchemy.

hoffrocket avatar Mar 27 '19 15:03 hoffrocket

This is somewhat old but still open. Has there been any additional development to support this in graphene-sqlalchemy, or are there authorization patterns the community has settled on as workable?

duboisj-illuminate avatar Feb 03 '21 19:02 duboisj-illuminate

We have an example of doing authorization with sqlalchemy-oso and graphene in this blog post.

This adheres to the "do it at the business layer" principle that the docs recommend, and might be helpful for folks who don't want to insert a ton of additional code into their graphql api.

We'd love to get feedback from people on how we could improve this!

samscott89 avatar May 09 '21 19:05 samscott89

Thank you!

duboisj-illuminate avatar May 27 '21 20:05 duboisj-illuminate