Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
graphene-django-plus
Advanced tools
DEPRECATION WARNING
Graphene itself is abandoned and most users are migrating to other better alternatives, like strawberry.
For that reason this lib is being deprecated and new features will no longer be developed for it. Maintenance is still going to happen and PRs are still welcomed though.
For anyone looking for alternatives, I created strawberry-django-plus to use not only as a migration path to the projects I maintain, but also to add even more awesome features. Be sure to check it out!
Tools to easily create permissioned CRUD endpoints in graphene-django.
pip install graphene-django-plus
To make use of everything this lib has to offer, it is recommended to install both graphene-django-optimizer and django-guardian.
pip install graphene-django-optimizer django-guardian
django forms
and drf serializers
)Check the docs for a complete api documentation.
graphene_django_plus.models.GuardedModel
: A django model that can be used
either directly or as a mixin. It will provide a .has_perm
method and a
.objects.for_user
that will be used by ModelType
described below to
check for object permissions.graphene_django_plus.types.ModelType
: This enchances
graphene_django_plus.DjangoModelType
by doing some automatic prefetch
optimization on setup and also checking for objects permissions on queries
when it inherits from GuardedModel
.
graphene_django_plus.fields.CountableConnection
: This enchances
graphene.relay.Connection
to provide a total_count
attribute.
Here is an example describing how to use those:
import graphene
from graphene import relay
from graphene_django.fields import DjangoConnectionField
from graphene_django_plus.models import GuardedModel
from graphene_django_plus.types import ModelType
from graphene_django_plus.fields import CountableConnection
class MyModel(GuardedModel):
class Meta:
# guardian permissions for this model
permissions = [
('can_read', "Can read the this object's info."),
]
name = models.CharField(max_length=255)
class MyModelType(ModelType):
class Meta:
model = MyModel
interfaces = [relay.Node]
# Use our CountableConnection
connection_class = CountableConnection
# When adding this to a query, only objects with a `can_read`
# permission to the request's user will be allowed to return to him
# Note that `can_read` was defined in the model.
# If the model doesn't inherid from `GuardedModel`, `guardian` is not
# installed or this list is empty, any object will be allowed.
# This is empty by default
object_permissions = [
'can_read',
]
# If unauthenticated users should be allowed to retrieve any object
# of this type. This is not dependent on `GuardedModel` and neither
# `guardian` and is defined as `False` by default
public = False
# A list of Django model permissions to check. Different from
# object_permissions, this uses the basic Django's permission system
# and thus is not dependent on `GuardedModel` and neither `guardian`.
# This is an empty list by default.
permissions = []
class Query(graphene.ObjectType):
my_models = DjangoConnectionField(MyModelType)
my_model = relay.Node.Field(MyModelType)
This can be queried like:
# All objects that the user has permission to see
query {
myModels {
totalCount
edges {
node {
id
name
}
}
}
}
# Single object if the user has permission to see it
query {
myModel(id: "<relay global ID>") {
id
name
}
}
graphene_django_plus.mutations.BaseMutation
: Base mutation using relay
and some basic permission checking. Just override its .perform_mutation
to
perform the mutation.
graphene_django_plus.mutations.ModelMutation
: Model mutation capable of
both creating and updating a model based on the existence of an id
attribute in the input. All the model's fields will be automatically read
from Django, inserted in the input type and validated.
graphene_django_plus.mutations.ModelCreateMutation
: A ModelMutation
enforcing a "create only" rule by excluding the id
field from the input.
graphene_django_plus.mutations.ModelUpdateMutation
: A ModelMutation
enforcing a "update only" rule by making the id
field required in the
input.
graphene_django_plus.mutations.ModelDeleteMutation
: A mutation that will
receive only the model's id and will delete it (if given permission, of
course).
Here is an example describing how to use those:
import graphene
from graphene import relay
from graphene_django_plus.models import GuardedModel
from graphene_django_plus.types import ModelType
from graphene_django_plus.mutations import (
ModelCreateMutation,
ModelUpdateMutation,
ModelDeleteMutation,
)
class MyModel(GuardedModel):
class Meta:
# guardian permissions for this model
permissions = [
('can_write', "Can update this object's info."),
]
name = models.CharField(max_length=255)
class MyModelType(ModelType):
class Meta:
model = MyModel
interfaces = [relay.Node]
class MyModelUpdateMutation(ModelUpdateMutation):
class Meta:
model = MyModel
# Make sure only users with the given permissions can modify the
# object.
# If the model doesn't inherid from `GuardedModel`, `guardian` is not
# installed on this list is empty, any object will be allowed.
# This is empty by default.
object_permissions = [
'can_write',
]
# If unauthenticated users should be allowed to retrieve any object
# of this type. This is not dependent on `GuardedModel` and neither
# `guardian` and is defined as `False` by default
public = False
# A list of Django model permissions to check. Different from
# object_permissions, this uses the basic Django's permission system
# and thus is not dependent on `GuardedModel` and neither `guardian`.
# This is an empty list by default.
permissions = []
class MyModelDeleteMutation(ModelDeleteMutation):
class Meta:
model = MyModel
object_permissions = [
'can_write',
]
class MyModelCreateMutation(ModelCreateMutation):
class Meta:
model = MyModel
@classmethod
def after_save(cls, info, instance, cleaned_input=None):
# If the user created the object, allow him to modify it
assign_perm('can_write', info.context.user, instance)
class Mutation(graphene.ObjectType):
my_model_create = MyModelCreateMutation.Field()
my_model_update = MyModelUpdateMutation.Field()
my_model_delete = MyModelDeleteMutation.Field()
This can be used to create/update/delete like:
# Create mutation
mutation {
myModelCreate(input: { name: "foobar" }) {
myModel {
name
}
errors {
field
message
}
}
}
# Update mutation
mutation {
myModelUpdate(input: { id: "<relay global ID>", name: "foobar" }) {
myModel {
name
}
errors {
field
message
}
}
}
# Delete mutation
mutation {
myModelDelete(input: { id: "<relay global ID>" }) {
myModel {
name
}
errors {
field
message
}
}
}
Any validation errors will be presented in the errors
return value.
To turn off auto related relations addition to the mutation input - set global
MUTATIONS_INCLUDE_REVERSE_RELATIONS
parameter to False
in your
settings.py
:
GRAPHENE_DJANGO_PLUS = {
'MUTATIONS_INCLUDE_REVERSE_RELATIONS': False
}
Note: in case reverse relation does not have related_name
attribute set -
mutation input will be generated as Django itself is generating by appending
_set
to the lower cased model name - modelname_set
This project is licensed under MIT licence (see LICENSE
for more info)
Make sure to have poetry installed.
Install dependencies with:
poetry install
Run the testsuite with:
poetry run pytest
Feel free to fork the project and send me pull requests with new features, corrections and translations. We'll gladly merge them and release new versions ASAP.
FAQs
Tools to easily create permissioned CRUD endpoints in graphene.
We found that graphene-django-plus demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.