kopia lustrzana https://github.com/OpenDroneMap/WebODM
Added default group logic with model wise permissions, tests, api project list with proper permissions check
rodzic
1381fce2a7
commit
550816ee78
|
@ -0,0 +1 @@
|
||||||
|
default_app_config = 'app.apps.MainConfig'
|
|
@ -1,6 +1,7 @@
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from rest_framework import serializers, viewsets
|
from rest_framework import serializers, viewsets, filters
|
||||||
from app import models
|
from app import models, permissions
|
||||||
|
from guardian.shortcuts import get_objects_for_user
|
||||||
|
|
||||||
class ProjectSerializer(serializers.HyperlinkedModelSerializer):
|
class ProjectSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
owner = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
|
owner = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
|
||||||
|
@ -13,5 +14,5 @@ class ProjectViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
"""
|
||||||
Projects the current user has access to.
|
Projects the current user has access to.
|
||||||
"""
|
"""
|
||||||
|
serializer_class = ProjectSerializer
|
||||||
queryset = models.Project.objects.all()
|
queryset = models.Project.objects.all()
|
||||||
serializer_class = ProjectSerializer
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
from rest_framework.test import APIClient
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
from app.models import Project
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
class TestApi(TestCase):
|
||||||
|
|
||||||
|
fixtures = ['test_users', ]
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
Project(
|
||||||
|
owner=User.objects.get(username="testuser"),
|
||||||
|
name="test project"
|
||||||
|
).save()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_project_list(self):
|
||||||
|
client = APIClient()
|
||||||
|
|
||||||
|
# Forbidden without credentials
|
||||||
|
res = client.get('/api/projects/')
|
||||||
|
self.assertEqual(res.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
client.login(username="testuser", password="test1234")
|
||||||
|
res = client.get('/api/projects/')
|
||||||
|
self.assertEqual(res.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertTrue(len(res.data.results) > 0)
|
||||||
|
|
||||||
|
res = client.get('/api/projects/1/')
|
||||||
|
self.assertEqual(res.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
res = client.get('/api/projects/dasjkldas/')
|
||||||
|
self.assertEqual(res.status_code, status.HTTP_404_NOT_FOUND)
|
|
@ -1,6 +1,11 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
from .boot import boot
|
||||||
|
|
||||||
class MainConfig(AppConfig):
|
class MainConfig(AppConfig):
|
||||||
name = 'main'
|
name = 'app'
|
||||||
|
verbose_name = 'Application'
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
boot()
|
|
@ -0,0 +1,24 @@
|
||||||
|
def boot():
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.contrib.auth.models import Permission
|
||||||
|
from django.contrib.auth.models import User, Group
|
||||||
|
from . import signals
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger('app.logger')
|
||||||
|
|
||||||
|
# Check default group
|
||||||
|
default_group, created = Group.objects.get_or_create(name='Default')
|
||||||
|
if created:
|
||||||
|
logger.info("Created default group")
|
||||||
|
|
||||||
|
# Add default permissions (view_project, change_project, delete_task, etc.)
|
||||||
|
for permission in ('_project', '_task'):
|
||||||
|
default_group.permissions.add(
|
||||||
|
*list(Permission.objects.filter(codename__endswith=permission))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check super user
|
||||||
|
if User.objects.count() == 0:
|
||||||
|
User.objects.create_superuser('admin', 'admin@example.com', 'admin')
|
||||||
|
logger.info("Created superuser")
|
|
@ -1,12 +1,9 @@
|
||||||
- model: app.project
|
- model: app.project
|
||||||
pk: 1
|
pk: 1
|
||||||
fields: {owner: 2, name: Test_Project2, description: This is a test project, created_at: ! '2016-09-12
|
fields: {owner: 2, name: Test_Project2, description: This is a test project, created_at: '2016-09-11T22:26:52.582858+00:00'}
|
||||||
10:08:39.045372+00:00'}
|
|
||||||
- model: app.project
|
- model: app.project
|
||||||
pk: 2
|
pk: 2
|
||||||
fields: {owner: 1, name: Test_Project1, description: This is a test project, created_at: ! '2016-09-12
|
fields: {owner: 1, name: Test_Project1, description: This is a test project, created_at: '2016-09-11T22:26:52.582858+00:00'}
|
||||||
10:08:58.533742+00:00'}
|
|
||||||
- model: app.project
|
- model: app.project
|
||||||
pk: 3
|
pk: 3
|
||||||
fields: {owner: 1, name: Test_Project3, description: This is a test project, created_at: ! '2016-09-12
|
fields: {owner: 1, name: Test_Project3, description: This is a test project, created_at: '2016-09-11T22:26:52.582858+00:00'}
|
||||||
10:11:31.357885+00:00'}
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
from rest_framework import permissions
|
||||||
|
|
||||||
|
class GuardianObjectPermissions(permissions.DjangoObjectPermissions):
|
||||||
|
"""
|
||||||
|
Similar to `DjangoObjectPermissions`, but adding 'view' permissions.
|
||||||
|
"""
|
||||||
|
perms_map = {
|
||||||
|
'GET': ['%(app_label)s.view_%(model_name)s'],
|
||||||
|
'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
|
||||||
|
'HEAD': ['%(app_label)s.view_%(model_name)s'],
|
||||||
|
'POST': ['%(app_label)s.add_%(model_name)s'],
|
||||||
|
'PUT': ['%(app_label)s.change_%(model_name)s'],
|
||||||
|
'PATCH': ['%(app_label)s.change_%(model_name)s'],
|
||||||
|
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
from django.db.models import signals
|
||||||
|
from django.dispatch import receiver
|
||||||
|
from django.contrib.auth.models import User, Group
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger('app.logger')
|
||||||
|
|
||||||
|
@receiver(signals.post_save, sender=User, dispatch_uid="user_check_default_group")
|
||||||
|
def check_default_group(sender, instance, created, **kwargs):
|
||||||
|
if created:
|
||||||
|
try:
|
||||||
|
default_group = Group.objects.get(name="Default")
|
||||||
|
instance.groups.add(default_group)
|
||||||
|
instance.save()
|
||||||
|
logger.info("Added {} to default group".format(instance.username))
|
||||||
|
except:
|
||||||
|
pass # Group "Default" is not available, probably loading fixtures at this moment...
|
17
app/tests.py
17
app/tests.py
|
@ -5,11 +5,19 @@ from django.contrib import messages
|
||||||
from django.test import Client
|
from django.test import Client
|
||||||
|
|
||||||
from .models import Project, Task
|
from .models import Project, Task
|
||||||
|
from .boot import boot
|
||||||
|
|
||||||
|
import api.tests
|
||||||
|
|
||||||
class TestApp(TestCase):
|
class TestApp(TestCase):
|
||||||
|
|
||||||
fixtures = ['test_users', 'test_processingnodes', ]
|
fixtures = ['test_users', 'test_processingnodes', ]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(TestApp, cls).setUpClass()
|
||||||
|
boot()
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.credentials = {
|
self.credentials = {
|
||||||
'username': 'testuser',
|
'username': 'testuser',
|
||||||
|
@ -85,6 +93,15 @@ class TestApp(TestCase):
|
||||||
res = c.get('/processingnode/abc/')
|
res = c.get('/processingnode/abc/')
|
||||||
self.assertTrue(res.status_code == 404)
|
self.assertTrue(res.status_code == 404)
|
||||||
|
|
||||||
|
def test_default_group(self):
|
||||||
|
# It exists
|
||||||
|
self.assertTrue(Group.objects.filter(name='Default').count() == 1)
|
||||||
|
|
||||||
|
# Verify that all new users are assigned to default group
|
||||||
|
u = User.objects.create_user(username="default_user")
|
||||||
|
u.refresh_from_db()
|
||||||
|
self.assertTrue(u.groups.filter(name='Default').count() == 1)
|
||||||
|
|
||||||
def test_projects(self):
|
def test_projects(self):
|
||||||
# Get a normal user
|
# Get a normal user
|
||||||
user = User.objects.get(pk=2)
|
user = User.objects.get(pk=2)
|
||||||
|
|
5
start.sh
5
start.sh
|
@ -3,9 +3,4 @@ echo Running migrations
|
||||||
python manage.py makemigrations
|
python manage.py makemigrations
|
||||||
python manage.py migrate
|
python manage.py migrate
|
||||||
|
|
||||||
echo Creating default superuser...
|
|
||||||
|
|
||||||
# This will fail if the user is a duplicate
|
|
||||||
echo "from django.contrib.auth.models import User; User.objects.create_superuser('admin', 'admin@example.com', 'admin')" | python manage.py shell
|
|
||||||
|
|
||||||
python manage.py runserver 0.0.0.0:8000
|
python manage.py runserver 0.0.0.0:8000
|
|
@ -137,6 +137,44 @@ STATICFILES_DIRS = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
LOGGING = {
|
||||||
|
'version': 1,
|
||||||
|
'disable_existing_loggers': False,
|
||||||
|
'formatters': {
|
||||||
|
'verbose': {
|
||||||
|
'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
|
||||||
|
},
|
||||||
|
'simple': {
|
||||||
|
'format': '%(levelname)s %(message)s'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'filters': {
|
||||||
|
'require_debug_true': {
|
||||||
|
'()': 'django.utils.log.RequireDebugTrue',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'handlers': {
|
||||||
|
'console': {
|
||||||
|
'level': 'INFO',
|
||||||
|
'filters': ['require_debug_true'],
|
||||||
|
'class': 'logging.StreamHandler',
|
||||||
|
'formatter': 'simple'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'loggers': {
|
||||||
|
'django': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'propagate': True,
|
||||||
|
},
|
||||||
|
'app.logger': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': 'INFO',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# Auth
|
# Auth
|
||||||
LOGIN_REDIRECT_URL = '/dashboard/'
|
LOGIN_REDIRECT_URL = '/dashboard/'
|
||||||
LOGIN_URL = '/login/'
|
LOGIN_URL = '/login/'
|
||||||
|
@ -154,7 +192,10 @@ MESSAGE_TAGS = {
|
||||||
# Use Django's standard django.contrib.auth permissions (no anonymous usage)
|
# Use Django's standard django.contrib.auth permissions (no anonymous usage)
|
||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
'DEFAULT_PERMISSION_CLASSES': [
|
'DEFAULT_PERMISSION_CLASSES': [
|
||||||
'rest_framework.permissions.DjangoModelPermissions',
|
'app.permissions.GuardianObjectPermissions',
|
||||||
|
],
|
||||||
|
'DEFAULT_FILTER_BACKENDS': [
|
||||||
|
'rest_framework.filters.DjangoObjectPermissionsFilter',
|
||||||
],
|
],
|
||||||
'PAGE_SIZE': 10,
|
'PAGE_SIZE': 10,
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue