A package to add field permission support for Graphene
Project description
# Graphene Field Permission
A package to add field-level permissions for [graphene-django](https://github.com/graphql-python/graphene-django).
## Use
On schema nodes add a decorator "\@has_field_access" to a resolve for each field that you want checked.
Usage example in schema files:
```python
from graphene_field_permission.decorators import has_field_access
class GroupNode(DjangoObjectType):
@has_field_access('permission1')
def resolve_group_name(self, info):
return self.name
class Meta:
model = Group
...
```
Example showing checking for one of multiple (unlimited) permissions:
```python
from graphene_field_permission.decorators import has_field_access
class GroupNode(DjangoObjectType):
@has_field_access('permission1', 'permission2')
def resolve_group_description(self, info):
return self.description
```
Example showing checking for one of multiple permissions under a group, for cases where permissions differ by group id:
```python
from graphene_field_permission.decorators import has_field_access
class GroupNode(DjangoObjectType):
@has_field_access('permission1', 'permission2', filter_field='group_id')
def resolve_group_text(self, info):
return self.text
```
```group_id``` in the above example will look at the GroupNode group_id field. Add ```.``` separators for related objects. ```filter_fields``` processing will traverse related objects as necessary to reduce the number of queries.
It's recommended to try and reduce these as much as possible. e.g. using group.division.corporation_id instead of group.division.corporation.id
```python
@has_field_access('permission1', 'permission2', filter_field='group.division.corporation_id')
```
## Result in GraphQL output:
```javascript
{
"errors": [
{
"message": "No access for user on field 'group_name'",
"locations": [
{
"line": 7,
"column": 9
}
],
"path": [
"group",
"edges",
0,
"node",
"groupName"
]
}
],
"data": {
"group": {
"edges": [
{
"node": null
}
]
}
}
}
```
### Usage notes:
1. An exception is thrown should a user attempt to access a field for which they don't have access. Graphene-django doesn't allow returning None for fields which aren't set as nullable. That makes it necessary to have your graphql queries fine grained enough to not call those fields in the first place. Client side checking of permissions is recommended in order to limit the field's accessed in the query in the first place.
1. I tried about four different ways to do this so resolve_field wasn't necessary, but found this to be the best balance of making it schema-definable and performant. I'm open to pull requests if someone can think of a better way.
## Installation
```
pip install graphene-field-permission
```
## Setup
1. Set up graphene and graphene django following their own instructions.
1. Create a file that will return permissions allowed for the user as shown below. By default lists and dicts containing lists are supported. That capability can be overridden by the user fairly easily by using your own "has_field_access" decorator and any data structure you prefer.
1. Update settings.py to match the instructions below.
### Example permissions population
These get called once per graphql call. Recommended to use Django ORM's ```select_related``` on queries where necessary in order to minimise the number of queries.
Standard:
```python
def get_user_permissions(user):
# query database to determine the passed in user's permissions
return ['permission1', 'permission2', 'permission3']
```
Or grouped:
```python
def get_user_permissions(user):
# query database to determine the passed in user's permissions
return {
'group-id-123': ['permission1', 'permission2', 'permission3'],
'group-id-456': ['permission1', 'permission3', 'permission5'],
}
```
### Settings
With the above method at app/helpers/user_permissions.py (for example) update settings.py to add:
```python
GRAPHENE_FIELD_PERMISSION = {
'SRC_MODULE': 'app.helpers.user_permissions',
'SRC_METHOD': 'get_user_permissions',
}
```
Also update the main graphene settings to add the middleware.
```python
GRAPHENE = {
'MIDDLEWARE': [
'graphene_field_permission.permissions.PermissionsMiddleware'
]
}
```
## Future updates, notes
I don't plan to develop this a whole lot further. It has scratched my itch for now. I would like to add the following though:
1. Unit tests, may get added in time.
1. This currently only supports Graphene under Django. I'm open to others adding support for other graphene-python projects if they want to submit pull requests.
A package to add field-level permissions for [graphene-django](https://github.com/graphql-python/graphene-django).
## Use
On schema nodes add a decorator "\@has_field_access" to a resolve for each field that you want checked.
Usage example in schema files:
```python
from graphene_field_permission.decorators import has_field_access
class GroupNode(DjangoObjectType):
@has_field_access('permission1')
def resolve_group_name(self, info):
return self.name
class Meta:
model = Group
...
```
Example showing checking for one of multiple (unlimited) permissions:
```python
from graphene_field_permission.decorators import has_field_access
class GroupNode(DjangoObjectType):
@has_field_access('permission1', 'permission2')
def resolve_group_description(self, info):
return self.description
```
Example showing checking for one of multiple permissions under a group, for cases where permissions differ by group id:
```python
from graphene_field_permission.decorators import has_field_access
class GroupNode(DjangoObjectType):
@has_field_access('permission1', 'permission2', filter_field='group_id')
def resolve_group_text(self, info):
return self.text
```
```group_id``` in the above example will look at the GroupNode group_id field. Add ```.``` separators for related objects. ```filter_fields``` processing will traverse related objects as necessary to reduce the number of queries.
It's recommended to try and reduce these as much as possible. e.g. using group.division.corporation_id instead of group.division.corporation.id
```python
@has_field_access('permission1', 'permission2', filter_field='group.division.corporation_id')
```
## Result in GraphQL output:
```javascript
{
"errors": [
{
"message": "No access for user on field 'group_name'",
"locations": [
{
"line": 7,
"column": 9
}
],
"path": [
"group",
"edges",
0,
"node",
"groupName"
]
}
],
"data": {
"group": {
"edges": [
{
"node": null
}
]
}
}
}
```
### Usage notes:
1. An exception is thrown should a user attempt to access a field for which they don't have access. Graphene-django doesn't allow returning None for fields which aren't set as nullable. That makes it necessary to have your graphql queries fine grained enough to not call those fields in the first place. Client side checking of permissions is recommended in order to limit the field's accessed in the query in the first place.
1. I tried about four different ways to do this so resolve_field wasn't necessary, but found this to be the best balance of making it schema-definable and performant. I'm open to pull requests if someone can think of a better way.
## Installation
```
pip install graphene-field-permission
```
## Setup
1. Set up graphene and graphene django following their own instructions.
1. Create a file that will return permissions allowed for the user as shown below. By default lists and dicts containing lists are supported. That capability can be overridden by the user fairly easily by using your own "has_field_access" decorator and any data structure you prefer.
1. Update settings.py to match the instructions below.
### Example permissions population
These get called once per graphql call. Recommended to use Django ORM's ```select_related``` on queries where necessary in order to minimise the number of queries.
Standard:
```python
def get_user_permissions(user):
# query database to determine the passed in user's permissions
return ['permission1', 'permission2', 'permission3']
```
Or grouped:
```python
def get_user_permissions(user):
# query database to determine the passed in user's permissions
return {
'group-id-123': ['permission1', 'permission2', 'permission3'],
'group-id-456': ['permission1', 'permission3', 'permission5'],
}
```
### Settings
With the above method at app/helpers/user_permissions.py (for example) update settings.py to add:
```python
GRAPHENE_FIELD_PERMISSION = {
'SRC_MODULE': 'app.helpers.user_permissions',
'SRC_METHOD': 'get_user_permissions',
}
```
Also update the main graphene settings to add the middleware.
```python
GRAPHENE = {
'MIDDLEWARE': [
'graphene_field_permission.permissions.PermissionsMiddleware'
]
}
```
## Future updates, notes
I don't plan to develop this a whole lot further. It has scratched my itch for now. I would like to add the following though:
1. Unit tests, may get added in time.
1. This currently only supports Graphene under Django. I'm open to others adding support for other graphene-python projects if they want to submit pull requests.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file graphene-field-permission-0.0.3.tar.gz
.
File metadata
- Download URL: graphene-field-permission-0.0.3.tar.gz
- Upload date:
- Size: 4.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.5.2
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 8392d745dff82ed81191382115457e4301c710714efb99f0bcf1e9b55606f9d3 |
|
MD5 | f262ed70606ae0b82762ba7cdbfaf068 |
|
BLAKE2b-256 | 3c89cf6d328d0c96dcda035dbdbebff6c2b3bd45399f9ff17810d6bbda6cac18 |
File details
Details for the file graphene_field_permission-0.0.3-py3-none-any.whl
.
File metadata
- Download URL: graphene_field_permission-0.0.3-py3-none-any.whl
- Upload date:
- Size: 7.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.5.2
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 25da9a41d023b5953c238f56fc808bc4bf24d828c7831c5aea52a17d0f62f6a5 |
|
MD5 | d969c82234b9f0137649c23b31593f48 |
|
BLAKE2b-256 | 814f3695bf9b8e70bc706f58c62658954ac025db3fe793c0e91fbdf1ae4559ac |