Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions django_oapif/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,38 @@ def delete_model(self, _request: HttpRequest, obj: M) -> tuple[int, dict[str, in
return obj.delete()

def has_view_permission(self, _request: HttpRequest, _obj: M | None = None) -> bool:
"""Return True if the given request has permission to view objects in the collection,
or a given object if defined.
"""
return True

def has_add_permission(self, _request: HttpRequest, _obj: M | None = None) -> bool:
"""Return True if the given request has permission to create objects in the collection,
or a given object if defined.
"""
return True

def has_change_permission(self, _request: HttpRequest, _obj: M | None = None) -> bool:
"""Return True if the given request has permission to change objects in the collection,
or a given object if defined.
"""
return True

def has_delete_permission(self, _request: HttpRequest, _obj: M | None = None) -> bool:
"""Return True if the given request has permission to delete objects in the collection,
or a given object if defined.
"""
return True


class AllowAnyHandler(QueryHandler):
class AllowAny(QueryHandler):
"""Allows full access to everyone."""

pass


class AuthenticatedHandler[M: Model](QueryHandler):
"""Allows access only to authenticated users."""
class IsAuthenticated[M: Model](QueryHandler):
"""Allows full access to authenticated users only."""

def has_view_permission(self, request: HttpRequest, _obj: M | None = None) -> bool:
return bool(request.user and request.user.is_authenticated)
Expand All @@ -52,14 +66,16 @@ def has_delete_permission(self, request: HttpRequest, _obj: M | None = None) ->
return bool(request.user and request.user.is_authenticated)


class AuthenticatedOrReadOnlyHandler[M: Model](AuthenticatedHandler):
"""Allows access only to authenticated users."""
class IsAuthenticatedOrReadOnly[M: Model](IsAuthenticated):
"""Allows full access to authenticated users only, but allows readonly access to everyone."""

def has_view_permission(self, _request: HttpRequest, _obj: M | None = None) -> bool:
return True


class DjangoModelPermissionsHandler[M: Model](QueryHandler):
class DjangoModelPermissions[M: Model](QueryHandler):
"""Reuses all Django permissions for a given model."""

def has_view_permission(self, request: HttpRequest, _obj: M | None = None) -> bool:
codename = get_permission_codename("view", self.opts)
return request.user.has_perm(f"{self.opts.app_label}.{codename}")
Expand All @@ -77,6 +93,8 @@ def has_delete_permission(self, request: HttpRequest, _obj: M | None = None) ->
return request.user.has_perm(f"{self.opts.app_label}.{codename}")


class DjangoModelPermissionsOrAnonReadOnly[M: Model](DjangoModelPermissionsHandler):
class DjangoModelPermissionsOrAnonReadOnly[M: Model](DjangoModelPermissions):
"""Reuses all Django permissions for a given model, but allows readonly access to everyone."""

def has_view_permission(self, _request: HttpRequest, _obj: M | None = None) -> bool:
return True
8 changes: 4 additions & 4 deletions docs/content/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
members: [register]


<!-- ::: django_oapif.permissions
::: django_oapif.handler
options:
members:
- BasePermission
- QueryHandler
- AllowAny
- IsAuthenticated
- IsAdminUser
- IsAuthenticatedOrReadOnly
- DjangoModelPermissions
- DjangoModelPermissionsOrAnonReadOnly -->
- DjangoModelPermissionsOrAnonReadOnly
3 changes: 1 addition & 2 deletions docs/content/demo.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ docker compose up --build -d
docker compose exec django python manage.py collectstatic --no-input
docker compose exec django python manage.py migrate --no-input

# A convenience start-up Django command is there
# to populate the database with testdata
# A convenience start-up Django command is there to populate the database with testdata
docker compose exec django python manage.py populate_users
docker compose exec django python manage.py populate_data
```
Expand Down
1 change: 1 addition & 0 deletions docs/content/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ hide:
# Django-OAPIF

*Django-OAPIF* allows to easily expose your Django models through an OGC API Features endpoint.

It is based on Django Ninja.
22 changes: 15 additions & 7 deletions docs/content/permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,29 @@ hide:

# Custom authentication & permissions

By default the viewsets use [`DjangoModelPermissionsOrAnonReadOnly`] django_oapif.permissions.DjangoModelPermissionsOrAnonReadOnly.

This can be altered in the DRF settings by adapting `DEFAULT_PERMISSION_CLASSES`.
By default the viewsets use [`DjangoModelPermissionsOrAnonReadOnly`](api/#django_oapif.handler.DjangoModelPermissionsOrAnonReadOnly).

You can also add custom permissions when registering their corresponding viewsets, as [`permission_classes`] django_oapif.permissions.
Example in `models.py`:

```python
# models.py

from django.contrib.gis.db import models

class MyModel(models.Model):
...
```


```python
# ogc.py

from .models import MyModel
from django_oapif import OAPIF
from django_oapif.permissions import OAPIF, DjangoModelPermissionsOrAnonReadOnly
from django_oapif.handler import DjangoModelPermissionsOrAnonReadOnly

ogc_api = OAPIF()

@ogc_api.register(auth=DjangoModelPermissionsOrAnonReadOnly)
class MyModel(models.Model):
...
ogc_api.register(MyModel, handler=DjangoModelPermissionsOrAnonReadOnly)
```
42 changes: 29 additions & 13 deletions docs/content/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,44 @@ INSTALLED_APPS = [
]
```

Add this to your `urls.py` :
## Declare your models:

```python
urlpatterns += [
...,
path("oapif/", include(django_oapif.urls)),
...,
]
# models.py

from django.contrib.gis.db import models

class TestModel(models.Model):
name = models.CharField(max_length=10)
geom = models.PointField(srid=2056)

class OtherTestModel(models.Model):
id = models.CharField(max_length=10)
geom = models.PolygonField(srid=2056)
```

## Register your models with the decorator:
## Declare a new OAPIF and register your models:

```python
# models.py
# ogc.py

from django.contrib.gis.db import models
from .models import TestModel
from django_oapif import OAPIF

ogc_api = OAPIF()

@ogc_api.register(TestModel)
@ogc_api.register(OtherTestModel)
```

@ogc_api.register()
class TestingDecorator(models.Model):
name = models.CharField(max_length=10)
geom = models.PointField(srid=2056)

## Add the API to the Django URLs:
```python
# urls.py

urlpatterns += [
...,
path("oapif/", include(django_oapif.urls)),
...,
]
```
4 changes: 2 additions & 2 deletions tests/django_oapif_tests/tests/ogc.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django_oapif import OAPIF
from django_oapif.handler import DjangoModelPermissionsHandler
from django_oapif.handler import DjangoModelPermissions, DjangoModelPermissionsOrAnonReadOnly

from .models import (
Line_2056_10fields,
Expand Down Expand Up @@ -70,5 +70,5 @@
"field_int",
*[f"field_str_{i}" for i in range(10)],
],
handler=DjangoModelPermissionsHandler,
handler=DjangoModelPermissions,
)
Loading