stash
This commit is contained in:
parent
8d55b00027
commit
ea27165e25
13 changed files with 162 additions and 34 deletions
19
core/authentication/admin.py
Normal file
19
core/authentication/admin.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
from django.contrib import admin
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from authentication.models import ExtendedUser, EventPermission, ExtendedAuthToken, AuthTokenEventPermissions
|
||||
|
||||
|
||||
class ExtendedUserAdmin(UserAdmin):
|
||||
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff', 'is_superuser')
|
||||
search_fields = ('username', 'email', 'first_name', 'last_name')
|
||||
ordering = ('username',)
|
||||
filter_horizontal = ('groups', 'user_permissions', 'permissions')
|
||||
|
||||
def permissions(self, obj):
|
||||
return ', '.join(obj.get_all_permissions())
|
||||
|
||||
|
||||
admin.site.register(ExtendedUser, ExtendedUserAdmin)
|
|
@ -1,5 +1,7 @@
|
|||
from rest_framework import routers, viewsets, serializers, permissions
|
||||
from rest_framework.decorators import api_view, permission_classes, authentication_classes
|
||||
from rest_framework.authtoken.serializers import AuthTokenSerializer
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from django.contrib.auth import login
|
||||
from django.urls import path
|
||||
|
@ -7,7 +9,6 @@ from django.dispatch import receiver
|
|||
from django.db.models.signals import post_save
|
||||
from knox.models import AuthToken
|
||||
from knox.views import LoginView as KnoxLoginView
|
||||
from rest_framework.authtoken.serializers import AuthTokenSerializer
|
||||
|
||||
from authentication.models import ExtendedUser
|
||||
|
||||
|
@ -30,6 +31,7 @@ class UserViewSet(viewsets.ModelViewSet):
|
|||
|
||||
|
||||
@api_view(['POST'])
|
||||
@permission_classes([IsAuthenticated])
|
||||
def selfUser(request):
|
||||
serializer = UserSerializer(request.user)
|
||||
return Response(serializer.data, status=200)
|
||||
|
|
|
@ -10,6 +10,7 @@ class PermissionsTestCase(TestCase):
|
|||
def setUp(self):
|
||||
super().setUp()
|
||||
self.user = ExtendedUser.objects.create_user('testuser', 'test', 'test')
|
||||
self.user.user_permissions.add(*Permission.objects.all())
|
||||
event1 = Event.objects.create(slug='testevent1', name='testevent1')
|
||||
event2 = Event.objects.create(slug='testevent2', name='testevent2')
|
||||
permission1 = Permission.objects.get(codename='view_event')
|
||||
|
@ -17,6 +18,9 @@ class PermissionsTestCase(TestCase):
|
|||
EventPermission.objects.create(user=self.user, permission=permission1, event=event2)
|
||||
self.token = AuthToken.objects.create(user=self.user)
|
||||
self.client = Client(headers={'Authorization': 'Token ' + self.token[1]})
|
||||
self.newuser = ExtendedUser.objects.create_user('newuser', 'test', 'test')
|
||||
self.newuser_token = AuthToken.objects.create(user=self.newuser)
|
||||
self.newuser_client = Client(headers={'Authorization': 'Token ' + self.newuser_token[1]})
|
||||
|
||||
def test_user_permissions(self):
|
||||
"""
|
||||
|
@ -24,7 +28,7 @@ class PermissionsTestCase(TestCase):
|
|||
"""
|
||||
response = self.client.get('/api/2/users/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.json()), 2)
|
||||
self.assertEqual(len(response.json()), 3)
|
||||
self.assertEqual(response.json()[0]['username'], 'legacy_user')
|
||||
self.assertEqual(response.json()[0]['email'], 'mail@localhost')
|
||||
self.assertEqual(response.json()[0]['first_name'], '')
|
||||
|
@ -34,3 +38,54 @@ class PermissionsTestCase(TestCase):
|
|||
self.assertEqual(response.json()[1]['email'], 'test')
|
||||
self.assertEqual(response.json()[1]['first_name'], '')
|
||||
self.assertEqual(response.json()[1]['last_name'], '')
|
||||
|
||||
def test_user_permission(self):
|
||||
"""
|
||||
Test that a user can only access their own data.
|
||||
"""
|
||||
#ä['add_logentry', 'change_logentry', 'delete_logentry', 'view_logentry', 'add_group', 'change_group',
|
||||
#ä 'delete_group', 'view_group', 'add_permission', 'change_permission', 'delete_permission', 'view_permission',
|
||||
#ä 'add_authtokeneventpermissions', 'change_authtokeneventpermissions', 'delete_authtokeneventpermissions',
|
||||
#ä 'view_authtokeneventpermissions', 'add_eventpermission', 'change_eventpermission', 'delete_eventpermission',
|
||||
#ä 'view_eventpermission', 'add_extendedauthtoken', 'change_extendedauthtoken', 'delete_extendedauthtoken',
|
||||
#ä 'view_extendedauthtoken', 'add_extendeduser', 'change_extendeduser', 'delete_extendeduser',
|
||||
#ä 'view_extendeduser', 'add_contenttype', 'change_contenttype', 'delete_contenttype', 'view_contenttype',
|
||||
#ä 'add_file', 'change_file', 'delete_file', 'view_file', 'add_container', 'change_container', 'delete_container',
|
||||
#ä 'view_container', 'add_event', 'change_event', 'delete_event', 'view_event', 'add_item', 'change_item',
|
||||
#ä 'delete_item', 'match_item', 'view_item', 'add_authtoken', 'change_authtoken', 'delete_authtoken',
|
||||
#ä 'view_authtoken', 'add_email', 'change_email', 'delete_email', 'view_email', 'add_eventaddress',
|
||||
#ä 'change_eventaddress', 'delete_eventaddress', 'view_eventaddress', 'add_systemevent', 'change_systemevent',
|
||||
#ä 'delete_systemevent', 'view_systemevent', 'add_session', 'change_session', 'delete_session', 'view_session',
|
||||
#ä 'add_comment', 'change_comment', 'delete_comment', 'view_comment', 'add_issuethread', 'change_issuethread',
|
||||
#ä 'delete_issuethread', 'send_mail', 'view_issuethread', 'add_statechange', 'change_statechange',
|
||||
#ä 'delete_statechange', 'view_statechange']
|
||||
|
||||
user = ExtendedUser.objects.create_user('testuser2', 'test', 'test')
|
||||
user.event_permissions.create(permission=Permission.objects.get(codename='view_item'), event=Event.objects.get(slug='testevent1'))
|
||||
user.event_permissions.create(permission=Permission.objects.get(codename='view_item'), event=Event.objects.get(slug='testevent2'))
|
||||
user.event_permissions.create(permission=Permission.objects.get(codename='add_item'), event=Event.objects.get(slug='testevent1'))
|
||||
user.save()
|
||||
print(user.get_all_permissions())
|
||||
#self.assertTrue(user.has_perm('inventory.view_event', Event.objects.get(slug='testevent1')))
|
||||
#self.assertTrue(user.has_perm('inventory.view_event', Event.objects.get(slug='testevent2')))
|
||||
#self.assertFalse(user.has_perm('inventory.add_event', Event.objects.get(slug='testevent1')))
|
||||
#self.assertFalse(user.has_perm('inventory.add_event', Event.objects.get(slug='testevent2')))
|
||||
|
||||
def test_item_api_permissions(self):
|
||||
"""
|
||||
Test that a user can only access their own data.
|
||||
"""
|
||||
response = self.client.get('/api/2/testevent1/items/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.json()), 0)
|
||||
|
||||
response = self.client.get('/api/2/testevent2/items/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.json()), 0)
|
||||
|
||||
response = self.newuser_client.get('/api/2/testevent1/items/')
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
response = self.newuser_client.get('/api/2/testevent2/items/')
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from django.test import TestCase, Client
|
||||
from django.contrib.auth.models import Permission
|
||||
|
||||
from knox.models import AuthToken
|
||||
|
||||
|
@ -10,6 +11,7 @@ class UserApiTest(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
self.user = ExtendedUser.objects.create_user('testuser', 'test', 'test')
|
||||
self.user.user_permissions.add(*Permission.objects.all())
|
||||
self.user.save()
|
||||
self.token = AuthToken.objects.create(user=self.user)
|
||||
self.client = Client(headers={'Authorization': 'Token ' + self.token[1]})
|
||||
|
|
|
@ -65,7 +65,8 @@ INSTALLED_APPS = [
|
|||
REST_FRAMEWORK = {
|
||||
'TEST_REQUEST_DEFAULT_FORMAT': 'json',
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': ('knox.auth.TokenAuthentication',),
|
||||
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),
|
||||
'DEFAULT_PERMISSION_CLASSES': (
|
||||
'rest_framework.permissions.IsAuthenticated', 'rest_framework.permissions.DjangoModelPermissions'),
|
||||
}
|
||||
|
||||
AUTH_USER_MODEL = 'authentication.ExtendedUser'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from django.test import TestCase, Client
|
||||
from django.contrib.auth.models import Permission
|
||||
|
||||
from authentication.models import ExtendedUser
|
||||
from files.models import File
|
||||
|
@ -11,6 +12,7 @@ class FileTestCase(TestCase):
|
|||
def setUp(self):
|
||||
super().setUp()
|
||||
self.user = ExtendedUser.objects.create_user('testuser', 'test', 'test')
|
||||
self.user.user_permissions.add(*Permission.objects.all())
|
||||
self.user.save()
|
||||
self.token = AuthToken.objects.create(user=self.user)
|
||||
self.client = Client(headers={'Authorization': 'Token ' + self.token[1]})
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
from datetime import datetime
|
||||
|
||||
from django.urls import path, re_path
|
||||
from django.contrib.auth.decorators import permission_required
|
||||
from rest_framework import routers, viewsets, serializers
|
||||
from rest_framework.decorators import api_view, permission_classes, authentication_classes
|
||||
from rest_framework.decorators import api_view, permission_classes
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
|
||||
from files.models import File
|
||||
from inventory.models import Event, Container, Item
|
||||
|
@ -37,7 +39,6 @@ class ContainerSerializer(serializers.ModelSerializer):
|
|||
class ContainerViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = ContainerSerializer
|
||||
queryset = Container.objects.all()
|
||||
permission_classes = []
|
||||
|
||||
|
||||
class ItemSerializer(serializers.ModelSerializer):
|
||||
|
@ -95,6 +96,8 @@ class ItemSerializer(serializers.ModelSerializer):
|
|||
|
||||
|
||||
@api_view(['GET'])
|
||||
@permission_classes([IsAuthenticated])
|
||||
@permission_required('inventory.view_item', raise_exception=True)
|
||||
def search_items(request, event_slug, query):
|
||||
try:
|
||||
event = Event.objects.get(slug=event_slug)
|
||||
|
@ -109,12 +112,17 @@ def search_items(request, event_slug, query):
|
|||
|
||||
|
||||
@api_view(['GET', 'POST'])
|
||||
@permission_classes([IsAuthenticated])
|
||||
def item(request, event_slug):
|
||||
try:
|
||||
event = Event.objects.get(slug=event_slug)
|
||||
if request.method == 'GET':
|
||||
if not request.user.has_event_perm(event, 'inventory.view_item'):
|
||||
return Response(status=403)
|
||||
return Response(ItemSerializer(Item.objects.filter(event=event), many=True).data)
|
||||
elif request.method == 'POST':
|
||||
if not request.user.has_event_perm(event, 'inventory.add_item'):
|
||||
return Response(status=403)
|
||||
validated_data = ItemSerializer(data=request.data)
|
||||
if validated_data.is_valid():
|
||||
validated_data.save(event=event)
|
||||
|
@ -124,18 +132,25 @@ def item(request, event_slug):
|
|||
|
||||
|
||||
@api_view(['GET', 'PUT', 'DELETE'])
|
||||
@permission_classes([IsAuthenticated])
|
||||
def item_by_id(request, event_slug, id):
|
||||
try:
|
||||
event = Event.objects.get(slug=event_slug)
|
||||
item = Item.objects.get(event=event, uid=id)
|
||||
if request.method == 'GET':
|
||||
if not request.user.has_event_perm(event, 'inventory.view_item'):
|
||||
return Response(status=403)
|
||||
return Response(ItemSerializer(item).data)
|
||||
elif request.method == 'PUT':
|
||||
if not request.user.has_event_perm(event, 'inventory.change_item'):
|
||||
return Response(status=403)
|
||||
validated_data = ItemSerializer(item, data=request.data)
|
||||
if validated_data.is_valid():
|
||||
validated_data.save()
|
||||
return Response(validated_data.data)
|
||||
elif request.method == 'DELETE':
|
||||
if not request.user.has_event_perm(event, 'inventory.delete_item'):
|
||||
return Response(status=403)
|
||||
item.delete()
|
||||
return Response(status=204)
|
||||
except Item.DoesNotExist:
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from django.test import TestCase, Client
|
||||
from django.contrib.auth.models import Permission
|
||||
from knox.models import AuthToken
|
||||
|
||||
from authentication.models import ExtendedUser
|
||||
|
@ -9,6 +10,7 @@ class ApiTest(TestCase):
|
|||
def setUp(self):
|
||||
super().setUp()
|
||||
self.user = ExtendedUser.objects.create_user('testuser', 'test', 'test')
|
||||
self.user.user_permissions.add(*Permission.objects.all())
|
||||
self.user.save()
|
||||
self.token = AuthToken.objects.create(user=self.user)
|
||||
self.client = Client(headers={'Authorization': 'Token ' + self.token[1]})
|
||||
|
|
|
@ -1,19 +1,27 @@
|
|||
from django.test import TestCase, Client
|
||||
from inventory.models import Container
|
||||
from django.contrib.auth.models import Permission
|
||||
from knox.models import AuthToken
|
||||
|
||||
client = Client()
|
||||
from authentication.models import ExtendedUser
|
||||
from inventory.models import Container
|
||||
|
||||
|
||||
class ContainerTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.user = ExtendedUser.objects.create_user('testuser', 'test', 'test')
|
||||
self.user.user_permissions.add(*Permission.objects.all())
|
||||
self.token = AuthToken.objects.create(user=self.user)
|
||||
self.client = Client(headers={'Authorization': 'Token ' + self.token[1]})
|
||||
|
||||
def test_empty(self):
|
||||
response = client.get('/api/2/boxes/')
|
||||
response = self.client.get('/api/2/boxes/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.json(), [])
|
||||
|
||||
def test_members(self):
|
||||
Container.objects.create(name='BOX')
|
||||
response = client.get('/api/2/boxes/')
|
||||
response = self.client.get('/api/2/boxes/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.json()), 1)
|
||||
self.assertEqual(response.json()[0]['cid'], 1)
|
||||
|
@ -24,12 +32,12 @@ class ContainerTestCase(TestCase):
|
|||
Container.objects.create(name='BOX 1')
|
||||
Container.objects.create(name='BOX 2')
|
||||
Container.objects.create(name='BOX 3')
|
||||
response = client.get('/api/2/boxes/')
|
||||
response = self.client.get('/api/2/boxes/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.json()), 3)
|
||||
|
||||
def test_create_container(self):
|
||||
response = client.post('/api/2/box/', {'name': 'BOX'})
|
||||
response = self.client.post('/api/2/box/', {'name': 'BOX'})
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(response.json()['cid'], 1)
|
||||
self.assertEqual(response.json()['name'], 'BOX')
|
||||
|
@ -39,9 +47,8 @@ class ContainerTestCase(TestCase):
|
|||
self.assertEqual(Container.objects.all()[0].name, 'BOX')
|
||||
|
||||
def test_update_container(self):
|
||||
from rest_framework.test import APIClient
|
||||
box = Container.objects.create(name='BOX 1')
|
||||
response = APIClient().put(f'/api/2/box/{box.cid}/', {'name': 'BOX 2'})
|
||||
response = self.client.put(f'/api/2/box/{box.cid}/', {'name': 'BOX 2'}, content_type='application/json')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.json()['cid'], 1)
|
||||
self.assertEqual(response.json()['name'], 'BOX 2')
|
||||
|
@ -54,6 +61,6 @@ class ContainerTestCase(TestCase):
|
|||
box = Container.objects.create(name='BOX 1')
|
||||
Container.objects.create(name='BOX 2')
|
||||
self.assertEqual(len(Container.objects.all()), 2)
|
||||
response = client.delete(f'/api/2/box/{box.cid}/')
|
||||
response = self.client.delete(f'/api/2/box/{box.cid}/')
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.assertEqual(len(Container.objects.all()), 1)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from django.test import TestCase, Client
|
||||
from django.contrib.auth.models import Permission
|
||||
from knox.models import AuthToken
|
||||
|
||||
from authentication.models import ExtendedUser
|
||||
|
@ -13,6 +14,7 @@ class ItemTestCase(TestCase):
|
|||
self.event = Event.objects.create(slug='EVENT', name='Event')
|
||||
self.box = Container.objects.create(name='BOX')
|
||||
self.user = ExtendedUser.objects.create_user('testuser', 'test', 'test')
|
||||
self.user.user_permissions.add(*Permission.objects.all())
|
||||
self.token = AuthToken.objects.create(user=self.user)
|
||||
self.client = Client(headers={'Authorization': 'Token ' + self.token[1]})
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import inspect
|
||||
from unittest import mock
|
||||
from knox.models import AuthToken
|
||||
|
||||
from django.test import TestCase, Client
|
||||
from django.contrib.auth.models import Permission
|
||||
from knox.models import AuthToken
|
||||
|
||||
from authentication.models import ExtendedUser
|
||||
from core.settings import MAIL_DOMAIN
|
||||
|
@ -28,6 +29,7 @@ class EmailsApiTest(TestCase):
|
|||
def setUp(self):
|
||||
super().setUp()
|
||||
self.user = ExtendedUser.objects.create_user('testuser', 'test', 'test')
|
||||
self.user.user_permissions.add(*Permission.objects.all())
|
||||
self.user.save()
|
||||
self.token = AuthToken.objects.create(user=self.user)
|
||||
self.client = Client(headers={'Authorization': 'Token ' + self.token[1]})
|
||||
|
@ -62,6 +64,7 @@ class LMTPHandlerTestCase(TestCase): # TODO replace with less hacky test
|
|||
def setUp(self):
|
||||
super().setUp()
|
||||
self.user = ExtendedUser.objects.create_user('testuser', 'test', 'test')
|
||||
self.user.user_permissions.add(*Permission.objects.all())
|
||||
self.user.save()
|
||||
self.token = AuthToken.objects.create(user=self.user)
|
||||
self.client = Client(headers={'Authorization': 'Token ' + self.token[1]})
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import logging
|
||||
|
||||
from django.urls import path
|
||||
from rest_framework.decorators import api_view, permission_classes, authentication_classes
|
||||
from django.contrib.auth.decorators import permission_required
|
||||
from rest_framework import routers, viewsets, serializers, status
|
||||
from rest_framework.decorators import api_view, permission_classes
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from asgiref.sync import async_to_sync
|
||||
|
||||
|
@ -56,6 +58,8 @@ class IssueViewSet(viewsets.ModelViewSet):
|
|||
|
||||
|
||||
@api_view(['POST'])
|
||||
@permission_classes([IsAuthenticated])
|
||||
@permission_required('tickets.add_issuethread', raise_exception=True)
|
||||
def reply(request, pk):
|
||||
issue = IssueThread.objects.get(pk=pk)
|
||||
# email = issue.reply(request.data['body']) # TODO evaluate if this is a useful abstraction
|
||||
|
|
|
@ -20,11 +20,20 @@ axios.interceptors.response.use(response => response, error => {
|
|||
return axios.request(error.config);
|
||||
}
|
||||
});
|
||||
} else
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
axios.interceptors.response.use(response => response, error => {
|
||||
} else if (error.response.status === 403) {
|
||||
const message = `
|
||||
<h3>Access denied.</h3>
|
||||
<p>
|
||||
url: ${error.config.url}
|
||||
<br>
|
||||
method: ${error.config.method}
|
||||
<br>
|
||||
response-body: ${error.response && error.response.body}
|
||||
</p>
|
||||
`;
|
||||
store.commit('createToast', {title: 'Error: Access denied', message, color: 'danger'});
|
||||
return Promise.reject(error)
|
||||
} else {
|
||||
console.log('error interceptor fired');
|
||||
console.error(error); // todo: toast error
|
||||
console.log(Object.entries(error));
|
||||
|
@ -45,6 +54,7 @@ axios.interceptors.response.use(response => response, error => {
|
|||
store.commit('createToast', {title: 'Error: Unknown', message: error.toString(), color: 'danger'});
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
});
|
||||
|
||||
const store = new Vuex.Store({
|
||||
|
@ -226,8 +236,12 @@ const store = new Vuex.Store({
|
|||
router.push({path: `/${getters.getEventSlug}/items/`, query: {box}});
|
||||
},
|
||||
async loadEventItems({commit, getters}) {
|
||||
try {
|
||||
const {data} = await axios.get(`/2/${getters.getEventSlug}/items/`);
|
||||
commit('replaceLoadedItems', data);
|
||||
} catch (e) {
|
||||
console.error("Error loading items");
|
||||
}
|
||||
},
|
||||
async searchEventItems({commit, getters}, query) {
|
||||
const foo = utf8.encode(query);
|
||||
|
|
Loading…
Reference in a new issue