Skip to content

Commit

Permalink
feat: use dgap instead of custom visibilities and permissions (#289)
Browse files Browse the repository at this point in the history
* feat!: use dgap instead of custom visibilities and permissions

BREAKING CHANGE: This removes the custom permission system for DGAP.

Change imports for the visibility, permission and validators
Example:
From
```py
from alexandria.core.visibilities import BaseVisibility, filter_queryset_for
```
to
```py
from generic_permissions.visibilities import, filter_queryset_for
```

* chore: update readme

* fix: cleanup dgap merge
  • Loading branch information
Yelinz authored Nov 17, 2023
1 parent a218d02 commit 2a92203
Show file tree
Hide file tree
Showing 22 changed files with 1,170 additions and 1,269 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@ make load_example_data

### Configuration

Document Merge Service is a [12factor app](https://12factor.net/) which means that configuration is stored in environment variables.
Alexandria is a [12factor app](https://12factor.net/) which means that configuration is stored in environment variables.
Different environment variable types are explained at [django-environ](https://github.com/joke2k/django-environ#supported-types).

Additional authorization and validation of the models is handled by [DGAP](https://github.com/adfinis/django-generic-api-permissions/?tab=readme-ov-file#usage---for-people-deploying-a-dgap-equipped-app).


#### Common

A list of configuration options which you need
Expand All @@ -68,10 +71,10 @@ A list of configuration options which you need
- `ALEXANDRIA_CREATED_BY_USER_PROPERTY`: Overwrite the default user property which is used for `..._by_user` (default: username)
- `ALEXANDRIA_CREATED_BY_GROUP_PROPERTY`: Overwrite the default group property which is used for `..._by_group` (default: group)
- Authorization configurations
- `ALEXANDRIA_VISIBILITY_CLASSES`: Comma-separated list of classes that define visibility for all models
- `ALEXANDRIA_PERMISSION_CLASSES`: Comma-separated list of classes that define permissions for all models
- `ALEXANDRIA_VISIBILITY_CLASSES`: Comma-separated list of [DGAP](https://github.com/adfinis/django-generic-api-permissions/?tab=readme-ov-file#visibilities) classes that define visibility for all models
- `ALEXANDRIA_PERMISSION_CLASSES`: Comma-separated list of [DGAP](https://github.com/adfinis/django-generic-api-permissions/?tab=readme-ov-file#permissions) classes that define permissions for all models
- Data validation configuration
- `ALEXANDRIA_VALIDATION_CLASSES`: Comma-separated list of classes that define [custom validations](docs/validation.md)
- `ALEXANDRIA_VALIDATION_CLASSES`: Comma-separated list of [DGAP](https://github.com/adfinis/django-generic-api-permissions/?tab=readme-ov-file#data-validation) classes that define custom validations
- Thumbnail configuration (optional)
- `ALEXANDRIA_ENABLE_THUMBNAIL_GENERATION`: Set to `false` to disable thumbnail generation
- Check the docker-compose file for an example on how to set up generation with s3 hooks
Expand Down
23 changes: 11 additions & 12 deletions alexandria/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from io import BytesIO

import pytest
from django.apps import apps
from django.core.cache import cache
from factory.base import FactoryMetaClass
from minio import Minio
Expand All @@ -15,8 +16,6 @@
from rest_framework.test import APIClient
from urllib3 import HTTPResponse

from alexandria.core.models import VisibilityMixin
from alexandria.core.serializers import BaseSerializer
from alexandria.core.storage_clients import Minio as MinioStorageClient
from alexandria.core.tests import file_data
from alexandria.oidc_auth.models import OIDCUser
Expand Down Expand Up @@ -78,18 +77,18 @@ def _autoclear_cache():
cache.clear()


@pytest.fixture
def reset_visibilities():
before = VisibilityMixin.visibility_classes
yield
VisibilityMixin.visibility_classes = before
@pytest.fixture(autouse=True)
def reset_config_classes(settings):
"""
Reset the config classes to clean state after test.
The config classes need to be reset after running tests that
use them. Otherwise, unrelated tests may get affected.
"""

@pytest.fixture
def reset_validators():
before = BaseSerializer.validation_classes
yield
BaseSerializer.validation_classes = before
# First, set config to original value
core_config = apps.get_app_config("generic_permissions")
core_config.ready()


@pytest.fixture
Expand Down
19 changes: 0 additions & 19 deletions alexandria/core/apps.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,6 @@
from django.apps import AppConfig
from django.conf import settings
from django.utils.module_loading import import_string


class DefaultConfig(AppConfig):
name = "alexandria.core"
label = "alexandria_core"

def ready(self):
# to avoid recursive import error, load extension classes
# only once the app is ready
from .models import PermissionMixin, VisibilityMixin
from .serializers import BaseSerializer

PermissionMixin.permission_classes = [
import_string(cls) for cls in settings.ALEXANDRIA_PERMISSION_CLASSES
]
VisibilityMixin.visibility_classes = [
import_string(cls) for cls in settings.ALEXANDRIA_VISIBILITY_CLASSES
]

BaseSerializer.validation_classes = [
import_string(cls) for cls in settings.ALEXANDRIA_VALIDATION_CLASSES
]
6 changes: 0 additions & 6 deletions alexandria/core/collections.py

This file was deleted.

24 changes: 4 additions & 20 deletions alexandria/core/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,7 @@ class Migration(migrations.Migration):
),
],
options={"abstract": False},
bases=(
alexandria.core.models.PermissionMixin,
alexandria.core.models.VisibilityMixin,
models.Model,
),
bases=(models.Model,),
),
migrations.CreateModel(
name="Document",
Expand Down Expand Up @@ -192,11 +188,7 @@ class Migration(migrations.Migration):
),
],
options={"abstract": False},
bases=(
alexandria.core.models.PermissionMixin,
alexandria.core.models.VisibilityMixin,
models.Model,
),
bases=(models.Model,),
),
migrations.CreateModel(
name="Tag",
Expand Down Expand Up @@ -266,11 +258,7 @@ class Migration(migrations.Migration):
),
],
options={"abstract": False},
bases=(
alexandria.core.models.PermissionMixin,
alexandria.core.models.VisibilityMixin,
models.Model,
),
bases=(models.Model,),
),
migrations.CreateModel(
name="File",
Expand Down Expand Up @@ -357,11 +345,7 @@ class Migration(migrations.Migration):
),
],
options={"ordering": ["-created_at"]},
bases=(
alexandria.core.models.PermissionMixin,
alexandria.core.models.VisibilityMixin,
models.Model,
),
bases=(models.Model,),
),
migrations.AddField(
model_name="document",
Expand Down
6 changes: 1 addition & 5 deletions alexandria/core/migrations/0004_tag_synonym_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,7 @@ class Migration(migrations.Migration):
options={
"abstract": False,
},
bases=(
alexandria.core.models.PermissionMixin,
alexandria.core.models.VisibilityMixin,
models.Model,
),
bases=(models.Model,),
),
migrations.AddField(
model_name="tag",
Expand Down
6 changes: 1 addition & 5 deletions alexandria/core/migrations/0010_mark.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,7 @@ class Migration(migrations.Migration):
options={
"abstract": False,
},
bases=(
alexandria.core.models.PermissionMixin,
alexandria.core.models.VisibilityMixin,
models.Model,
),
bases=(models.Model,),
),
migrations.AddField(
model_name="document",
Expand Down
42 changes: 1 addition & 41 deletions alexandria/core/models.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import re
import uuid

from django.core.exceptions import ImproperlyConfigured
from django.core.validators import RegexValidator
from django.db import models
from django.utils.translation import gettext_lazy as _
from localized_fields.fields import LocalizedCharField, LocalizedTextField
from rest_framework import exceptions

from .storage_clients import client

Expand All @@ -23,45 +21,7 @@ def make_uuid():
return uuid.uuid4()


class VisibilityMixin:
visibility_classes = None

@classmethod
def visibility_queryset_filter(cls, queryset, request, **kwargs):
if cls.visibility_classes is None:
raise ImproperlyConfigured(
"check that app `alexandria.core.apps.DefaultConfig` is part of your `INSTALLED_APPS`."
)

for visibility_class in cls.visibility_classes:
queryset = visibility_class().filter_queryset(cls, queryset, request)

return queryset


class PermissionMixin:
permission_classes = None

@classmethod
def check_permissions(cls, request, **kwargs):
if cls.permission_classes is None:
raise ImproperlyConfigured(
"check that app `alexandria.core.apps.DefaultConfig` is part of your `INSTALLED_APPS`."
)

for permission_class in cls.permission_classes:
if not permission_class().has_permission(cls, request):
raise exceptions.PermissionDenied()

def check_object_permissions(self, request):
for permission_class in self.permission_classes:
if not permission_class().has_object_permission(
self.__class__, request, self
):
raise exceptions.PermissionDenied()


class BaseModel(PermissionMixin, VisibilityMixin, models.Model):
class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
created_by_user = models.CharField(
_("created by user"), max_length=150, blank=True, null=True
Expand Down
142 changes: 0 additions & 142 deletions alexandria/core/permissions.py

This file was deleted.

Loading

0 comments on commit 2a92203

Please sign in to comment.