Added default group logic with model wise permissions, tests, api project list with proper permissions check

pull/29/head
Piero Toffanin 2016-10-07 19:07:47 -04:00
rodzic 1381fce2a7
commit 550816ee78
11 zmienionych plików z 166 dodań i 16 usunięć

Wyświetl plik

@ -0,0 +1 @@
default_app_config = 'app.apps.MainConfig'

Wyświetl plik

@ -1,6 +1,7 @@
from django.contrib.auth.models import User
from rest_framework import serializers, viewsets
from app import models
from rest_framework import serializers, viewsets, filters
from app import models, permissions
from guardian.shortcuts import get_objects_for_user
class ProjectSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
@ -13,5 +14,5 @@ class ProjectViewSet(viewsets.ModelViewSet):
"""
Projects the current user has access to.
"""
serializer_class = ProjectSerializer
queryset = models.Project.objects.all()
serializer_class = ProjectSerializer

37
app/api/tests.py 100644
Wyświetl plik

@ -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)

Wyświetl plik

@ -1,6 +1,11 @@
from __future__ import unicode_literals
from django.apps import AppConfig
from .boot import boot
class MainConfig(AppConfig):
name = 'main'
name = 'app'
verbose_name = 'Application'
def ready(self):
boot()

24
app/boot.py 100644
Wyświetl plik

@ -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")

Wyświetl plik

@ -1,12 +1,9 @@
- model: app.project
pk: 1
fields: {owner: 2, name: Test_Project2, description: This is a test project, created_at: ! '2016-09-12
10:08:39.045372+00:00'}
fields: {owner: 2, name: Test_Project2, description: This is a test project, created_at: '2016-09-11T22:26:52.582858+00:00'}
- model: app.project
pk: 2
fields: {owner: 1, name: Test_Project1, description: This is a test project, created_at: ! '2016-09-12
10:08:58.533742+00:00'}
fields: {owner: 1, name: Test_Project1, description: This is a test project, created_at: '2016-09-11T22:26:52.582858+00:00'}
- model: app.project
pk: 3
fields: {owner: 1, name: Test_Project3, description: This is a test project, created_at: ! '2016-09-12
10:11:31.357885+00:00'}
fields: {owner: 1, name: Test_Project3, description: This is a test project, created_at: '2016-09-11T22:26:52.582858+00:00'}

15
app/permissions.py 100644
Wyświetl plik

@ -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'],
}

17
app/signals.py 100644
Wyświetl plik

@ -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...

Wyświetl plik

@ -5,11 +5,19 @@ from django.contrib import messages
from django.test import Client
from .models import Project, Task
from .boot import boot
import api.tests
class TestApp(TestCase):
fixtures = ['test_users', 'test_processingnodes', ]
@classmethod
def setUpClass(cls):
super(TestApp, cls).setUpClass()
boot()
def setUp(self):
self.credentials = {
'username': 'testuser',
@ -85,6 +93,15 @@ class TestApp(TestCase):
res = c.get('/processingnode/abc/')
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):
# Get a normal user
user = User.objects.get(pk=2)

Wyświetl plik

@ -3,9 +3,4 @@ echo Running migrations
python manage.py makemigrations
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

Wyświetl plik

@ -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
LOGIN_REDIRECT_URL = '/dashboard/'
LOGIN_URL = '/login/'
@ -154,7 +192,10 @@ MESSAGE_TAGS = {
# Use Django's standard django.contrib.auth permissions (no anonymous usage)
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissions',
'app.permissions.GuardianObjectPermissions',
],
'DEFAULT_FILTER_BACKENDS': [
'rest_framework.filters.DjangoObjectPermissionsFilter',
],
'PAGE_SIZE': 10,
}