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

Question: How do you recommend enforcing authorization? #186

Open
hoffrocket opened this issue Mar 22, 2019 · 5 comments
Open

Question: How do you recommend enforcing authorization? #186

hoffrocket opened this issue Mar 22, 2019 · 5 comments
Labels

Comments

@hoffrocket
Copy link

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?

@Nabellaleen
Copy link
Collaborator

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.

@hoffrocket
Copy link
Author

hoffrocket commented Mar 27, 2019

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 graphql-python/graphene-django#79 (comment)

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.

@duboisj-illuminate
Copy link

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?

@samscott89
Copy link

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!

@duboisj-illuminate
Copy link

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants