torchbox / wagtail-grapple

A Wagtail app that makes building GraphQL endpoints a breeze!
https://wagtail-grapple.readthedocs.io/en/latest/
Other
152 stars 57 forks source link

Object type and/or field level authentication? #169

Closed PeterDekkers closed 3 years ago

PeterDekkers commented 3 years ago

Hello. Any pointers regarding applying (custom) authentication logic to object types or fields? Not quite sure where in the chain, if at all, this could be injected.

E.g. similarly to:

ruisaraiva19 commented 3 years ago

@PeterDekkers It should be straightforward to add authenticated logic on fields, for example:

from django.contrib.auth.models import User
from django.db import models

from grapple.types import GraphQLCollection, GraphQLForeignKey

from wagtail.core.fields import RichTextField
from wagtail.core.models import Page

class BlogPage(Page):
    body = RichTextField()
    author = models.ForeignKey(User)

class AuthorPage(Page):
    def my_posts(self, info, **kwargs):
        # context will reference to the Django request
        if not info.context.user.is_authenticated:
            return BlogPage.objects.none()
        else:
            return BlogPage.objects.filter(author=info.context.user)

    graphql_fields = [
        GraphQLCollection(
            GraphQLForeignKey,
            "my_posts",
            BlogPage
        ),
    ]
PeterDekkers commented 3 years ago

Thank you 👍🏻

PeterDekkers commented 3 years ago

Say @ruisaraiva19, I don’t suppose it’s possible to add authentication or other filtering to the default Wagtail fields that wagtail-grapple exposes? E.g. say I wanted to protect this query so only authenticated users have access:

{
  pages {
    { id }
  }
}

Essentially to prevent just anyone from querying all the pages of the site.

ruisaraiva19 commented 3 years ago

@PeterDekkers You should be able to add some custom middleware to do that:

And in your settings.py:

GRAPHENE = {
    # ...
    'MIDDLEWARE': [
        'your_app.middleware.AuthorizationMiddleware',
    ]
}

And in your your_app/middleware.py:

class AuthorizationMiddleware(object):
    def resolve(self, next, root, info, **args):
        if info.field_name == 'pages' and info.parent_type == 'Query' and not info.context.user.is_authenticated:
            return None
        return next(root, info, **args)

Let me know if this works for you.

PeterDekkers commented 3 years ago

Thanks again @ruisaraiva19, that should indeed work.

zerolab commented 3 years ago

Thank you @ruisaraiva19 This type of info is great for a "tips & tricks" section of the docs!

ruisaraiva19 commented 3 years ago

Yes indeed. I'll add some snippets of code later this week.

ruisaraiva19 commented 3 years ago

PR #177 was merged with Query level middleware support. Will be ready to use on the next release.

PeterDekkers commented 3 years ago

Yeah, nice work @ruisaraiva19. Thank you!