From 5f9ba3bcd0969f5d0e76a945da081f2e9e62c33b Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 18 Jan 2017 19:29:40 +0100 Subject: [PATCH 1/6] labAdmin: kill role kind We now treats doors as devices which means that we can kill the role_kind and the various can_use_door_now methods. So now OpenDoorByNFC requires the the token authentication as the credits API. --- labAdmin/admin.py | 2 +- .../migrations/0007_remove_role_role_kind.py | 19 ++++++++++ labAdmin/models.py | 37 ++----------------- labAdmin/tests.py | 15 ++++---- labAdmin/views.py | 28 +++++++++----- 5 files changed, 50 insertions(+), 51 deletions(-) create mode 100644 labAdmin/migrations/0007_remove_role_role_kind.py diff --git a/labAdmin/admin.py b/labAdmin/admin.py index 31bb59a..2bf3e99 100755 --- a/labAdmin/admin.py +++ b/labAdmin/admin.py @@ -22,7 +22,7 @@ def subscription(self, obj): admin.site.register(UserProfile, UserProfileAdmin) class RoleAdmin(admin.ModelAdmin): - list_display = ('name', 'role_kind','valid',) + list_display = ('name', 'valid',) ordering = ('name',) admin.site.register(Role, RoleAdmin) diff --git a/labAdmin/migrations/0007_remove_role_role_kind.py b/labAdmin/migrations/0007_remove_role_role_kind.py new file mode 100644 index 0000000..e665359 --- /dev/null +++ b/labAdmin/migrations/0007_remove_role_role_kind.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.1 on 2017-01-18 19:12 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('labAdmin', '0006_device_token'), + ] + + operations = [ + migrations.RemoveField( + model_name='role', + name='role_kind', + ), + ] diff --git a/labAdmin/models.py b/labAdmin/models.py index f374455..f03fa1a 100755 --- a/labAdmin/models.py +++ b/labAdmin/models.py @@ -93,16 +93,10 @@ def subscription_end(self): def subscriptionExpired(self): return self.needSubscription and self.endSubscription < timezone.now() - def can_open_door_now(self): - roles = self.groups.values_list('roles__pk', flat=True).distinct() - return TimeSlot.objects.can_now().filter( - role__in=roles, role__role_kind=0, role__valid=True - ).exists() - def can_use_device_now(self, device): roles = self.groups.values_list('roles__pk', flat=True).distinct() return TimeSlot.objects.can_now().filter( - role__in=roles, role__role_kind=1, role__valid=True + role__in=roles, role__valid=True ).exists() def displaygroups(self): @@ -128,17 +122,9 @@ class Group(models.Model): # define Many-To-Many fields roles=models.ManyToManyField('Role') - def can_open_door_now(self): - # Define groups and role - try: - return TimeSlot.objects.can_now().filter(role__group=self,role__role_kind=0,role__valid=True).exists() - except: - # Any Exception return False - return False - def can_use_device_now(self, device): try: - return TimeSlot.objects.can_now().filter(role__group=self,role__role_kind=1, role__category_device=device.category_device, role__valid=True).exists() + return TimeSlot.objects.can_now().filter(role__group=self, role__category_device=device.category_device, role__valid=True).exists() except: # Any Exception return False return False @@ -147,38 +133,23 @@ def __str__(self): return "%s" % (self.name) class Role(models.Model): - # define role kind choices - ROLE_KIND_CHOICES = ( - (0, "Door Access"), - (1, "Use Device"), - ) - name=models.CharField(max_length=50) - role_kind=models.IntegerField(choices=ROLE_KIND_CHOICES) time_slots=models.ManyToManyField(TimeSlot) valid=models.BooleanField(default=True) # define Many-To-Many Fieds categories=models.ManyToManyField('Category',blank=True) - def can_open_door_now(self): - # Define groups and role - try: - return TimeSlot.objects.can_now().filter(role=self, role__role_kind=0,role__valid=True).exists() - except: - # Any Exception return False - return False - def can_use_device_now(self, device): try: - return TimeSlot.objects.can_now().filter(role=self, role__role_kind=1, role__category_device=device.category_device, role__valid=True).exists() + return TimeSlot.objects.can_now().filter(role=self, role__category_device=device.category_device, role__valid=True).exists() except: # Any Exception Return False return False def __str__(self): - return "%s - %s" % (self.name, self.ROLE_KIND_CHOICES[self.role_kind][1]) + return self.name class Device(models.Model): name = models.CharField(max_length=100) diff --git a/labAdmin/tests.py b/labAdmin/tests.py index d8a51fe..2152e00 100755 --- a/labAdmin/tests.py +++ b/labAdmin/tests.py @@ -39,19 +39,16 @@ def setUpTestData(cls): fablab_role = Role.objects.create( name="Fablab Access", - role_kind=0 ) fablab_role.time_slots.add(daily_timeslot) guest_role = Role.objects.create( name="Guest Access", - role_kind=0 ) guest_role.time_slots.add(reduced_timeslot) full_devices_role = Role.objects.create( name="Devices Access", - role_kind=1 ) full_devices_role.time_slots.add(full_timeslot) @@ -129,11 +126,12 @@ def test_open_door_by_nfc(self): self.assertFalse(LogAccess.objects.all().exists()) client = Client() + auth = 'Token {}'.format(self.device.token) url = reverse('open-door-nfc') data = { 'nfc_id': self.card.nfc_id } - response = client.post(url, data, format='json') + response = client.post(url, data, format='json', HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 201) response_data = json.loads(str(response.content, encoding='utf8')) self.assertIn('users', response_data) @@ -143,19 +141,22 @@ def test_open_door_by_nfc(self): self.assertEqual(user_profile['name'], self.userprofile.name) self.assertEqual(response_data['type'], 'other') self.assertIn('datetime', response_data) - self.assertEqual(response_data['open'], self.userprofile.can_open_door_now()) + self.assertEqual(response_data['open'], self.userprofile.can_use_device_now(self.device)) users = UserProfile.objects.all() logaccess = LogAccess.objects.filter(users=users, card=self.card) self.assertTrue(logaccess.exists()) + response = client.post(url, data, format='json') + self.assertEqual(response.status_code, 403) + data = { 'nfc_id': 0 } - response = client.post(url, data, format='json') + response = client.post(url, data, format='json', HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 400) - response = client.get(url) + response = client.get(url, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 405) def test_timeslot_manager_now(self): diff --git a/labAdmin/views.py b/labAdmin/views.py index e0693b1..9e7d44b 100755 --- a/labAdmin/views.py +++ b/labAdmin/views.py @@ -45,19 +45,27 @@ class OpenDoorByNFC(APIView): If the nfc code isn't correct or valid, the API save in 'LogError' a new error that contains the error then return an alert message to client (HTTP_400_BAD_REQUEST) """ + + permission_classes = (DeviceTokenPermission,) + def post(self, request, format=None): - nfc = request.data.get('nfc_id') - users = UserProfile.objects.filter(card__nfc_id=nfc) - if not users.exists(): - LogError(description="Api: Open Door By NFC - NFC not Valid", code=nfc).save() + nfc_id = request.data.get('nfc_id') + try: + card = Card.objects.get(nfc_id=nfc_id) + except Card.DoesNotExist: + LogError(description="Api: Use Device - nfc ID not valid", code=nfc_id or '').save() return Response("", status=status.HTTP_400_BAD_REQUEST) - can_open = False - for u in users: - if u.can_open_door_now(): - can_open = True - break - card = users.first().card + token = get_token_from_request(request) + try: + device = Device.objects.get(token=token) + except Card.DoesNotExist: + LogError(description="Api: Open Door By NFC - token not valid", code=token or '').save() + return Response("", status=status.HTTP_400_BAD_REQUEST) + + user = card.userprofile + can_open = user.can_use_device_now(device) + users = UserProfile.objects.filter(pk=user.pk) log_access = LogAccess.objects.log(users=users, card=card, opened=can_open) users_pks = users.values_list('pk', flat=True) if Group.objects.filter(userprofile__in=users_pks, name__icontains='Fablab').exists(): From 858250edf47409bcb03f8688affb0348801214d9 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Wed, 18 Jan 2017 19:34:37 +0100 Subject: [PATCH 2/6] labAdmin: kill unused can_use_device_now We don't need them and probably they aren't working either. --- labAdmin/models.py | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/labAdmin/models.py b/labAdmin/models.py index f03fa1a..a2d08b5 100755 --- a/labAdmin/models.py +++ b/labAdmin/models.py @@ -122,15 +122,8 @@ class Group(models.Model): # define Many-To-Many fields roles=models.ManyToManyField('Role') - def can_use_device_now(self, device): - try: - return TimeSlot.objects.can_now().filter(role__group=self, role__category_device=device.category_device, role__valid=True).exists() - except: - # Any Exception return False - return False - def __str__(self): - return "%s" % (self.name) + return self.name class Role(models.Model): name=models.CharField(max_length=50) @@ -141,13 +134,6 @@ class Role(models.Model): # define Many-To-Many Fieds categories=models.ManyToManyField('Category',blank=True) - def can_use_device_now(self, device): - try: - return TimeSlot.objects.can_now().filter(role=self, role__category_device=device.category_device, role__valid=True).exists() - except: - # Any Exception Return False - return False - def __str__(self): return self.name From 206ec53819f817c50b70cabb096e9cbb1af6a1bc Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 24 Jan 2017 10:42:54 +0100 Subject: [PATCH 3/6] labAdmin: remove unused data from tests fixture --- labAdmin/tests.py | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/labAdmin/tests.py b/labAdmin/tests.py index 2152e00..15c3734 100755 --- a/labAdmin/tests.py +++ b/labAdmin/tests.py @@ -7,7 +7,7 @@ from django.utils import timezone, dateparse from .models import ( - Card, Group, LogAccess, Role, TimeSlot, UserProfile, TimeSlot, + Card, Group, LogAccess, Role, TimeSlot, UserProfile, LogCredits, Category, Device, LogDevice ) @@ -15,20 +15,6 @@ class TestLabAdmin(TestCase): @classmethod def setUpTestData(cls): - daily_timeslot = TimeSlot.objects.create( - name="thursday full day", - weekday_start=4, - weekday_end=4, - hour_start=dateparse.parse_time("8:0:0"), - hour_end=dateparse.parse_time("23:0:0"), - ) - reduced_timeslot = TimeSlot.objects.create( - name="thursday reduced", - weekday_start=4, - weekday_end=4, - hour_start=dateparse.parse_time("14:0:0"), - hour_end=dateparse.parse_time("20:0:0"), - ) full_timeslot = TimeSlot.objects.create( name="any day", weekday_start=1, @@ -37,25 +23,11 @@ def setUpTestData(cls): hour_end=dateparse.parse_time("23:59:59"), ) - fablab_role = Role.objects.create( - name="Fablab Access", - ) - fablab_role.time_slots.add(daily_timeslot) - - guest_role = Role.objects.create( - name="Guest Access", - ) - guest_role.time_slots.add(reduced_timeslot) - full_devices_role = Role.objects.create( name="Devices Access", ) full_devices_role.time_slots.add(full_timeslot) - fab_guest_group = Group.objects.create(name="Fablab Guest") - fab_guest_group.roles.add(fablab_role, guest_role) - guest_group = Group.objects.create(name="Guest") - guest_group.roles.add(guest_role) devices_group = Group.objects.create(name="Full Devices access") devices_group.roles.add(full_devices_role) @@ -68,7 +40,6 @@ def setUpTestData(cls): needSubscription=False, endSubscription=timezone.now() ) - u.groups.add(guest_group) u.groups.add(devices_group) noperm_card = Card.objects.create(nfc_id=654321) From fb9fcf294360de87d3e12bebd5617b91e2dbe592 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 24 Jan 2017 10:59:02 +0100 Subject: [PATCH 4/6] labAdmin: add tests for UserProfile.can_use_device_now() --- labAdmin/tests.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/labAdmin/tests.py b/labAdmin/tests.py index 15c3734..e65424a 100755 --- a/labAdmin/tests.py +++ b/labAdmin/tests.py @@ -452,3 +452,29 @@ def test_device_stop_use(self): self.assertJSONEqual(response.content.decode('utf-8'), { 'cost': 0 }) + + def test_user_can_use_device_now(self): + self.assertTrue(self.userprofile.can_use_device_now(self.device)) + + def test_user_can_use_device_now_invalid_role(self): + timeslot = TimeSlot.objects.create( + name="any day for invalid role", + weekday_start=1, + weekday_end=7, + hour_start=dateparse.parse_time("00:00:00"), + hour_end=dateparse.parse_time("23:59:59"), + ) + + role = Role.objects.create( + name="invalid role", + valid=False + ) + role.time_slots.add(timeslot) + + group = Group.objects.create(name="group with invalid role") + group.roles.add(role) + self.noperm_userprofile.groups.add(group) + self.assertFalse(self.noperm_userprofile.can_use_device_now(self.device)) + + def test_user_can_use_device_now_no_group(self): + self.assertFalse(self.noperm_userprofile.can_use_device_now(self.device)) From 58507111b866364225ed172bcc5a82f55f335c50 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 24 Jan 2017 11:03:51 +0100 Subject: [PATCH 5/6] labAdmin: remove commented routes in urls --- labAdmin/urls.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/labAdmin/urls.py b/labAdmin/urls.py index 4c11cf6..3715c14 100755 --- a/labAdmin/urls.py +++ b/labAdmin/urls.py @@ -2,6 +2,7 @@ from . import views + urlpatterns = [ url(r'^opendoorbynfc/$', views.OpenDoorByNFC.as_view(), name="open-door-nfc"), url(r'^updateUsers/$', views.tempUpdateUser.as_view()), @@ -10,11 +11,4 @@ url(r'^card/credits/$', views.CardCredits.as_view(), name='card-credits'), url(r'^device/use/start/$', views.DeviceStartUse.as_view(), name='device-use-start'), url(r'^device/use/stop/$', views.DeviceStopUse.as_view(), name='device-use-stop'), - # url(r'^nfc/(?P.+)/$', views.NfcLogin.as_view()), - # url(r'^nfc/', views.NfcLogin.as_view()), - # url(r'^getpermission/', views.GetPermission.as_view()), - # url(r'^usedevicelist/', views.LogdeviceUseList.as_view()), - # url(r'^getToken/', views.GetTokenExample.as_view()), - # url(r'^users/', views.UserList.as_view()), - # url(r'^repeat/', views.Repeat.as_view()), ] From 5af99ccf8b63120f5034275076751ea2a35ecf4b Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Tue, 24 Jan 2017 11:27:05 +0100 Subject: [PATCH 6/6] labAdmin: check categories in can_use_device_now So that we can implement per category of device permission and not just for roles. --- labAdmin/models.py | 2 +- labAdmin/tests.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/labAdmin/models.py b/labAdmin/models.py index a2d08b5..edb13f0 100755 --- a/labAdmin/models.py +++ b/labAdmin/models.py @@ -96,7 +96,7 @@ def subscriptionExpired(self): def can_use_device_now(self, device): roles = self.groups.values_list('roles__pk', flat=True).distinct() return TimeSlot.objects.can_now().filter( - role__in=roles, role__valid=True + role__in=roles, role__valid=True, role__categories=device.category ).exists() def displaygroups(self): diff --git a/labAdmin/tests.py b/labAdmin/tests.py index e65424a..064f649 100755 --- a/labAdmin/tests.py +++ b/labAdmin/tests.py @@ -55,6 +55,7 @@ def setUpTestData(cls): category = Category.objects.create( name="category" ) + full_devices_role.categories.add(category) device = Device.objects.create( name="device", @@ -478,3 +479,22 @@ def test_user_can_use_device_now_invalid_role(self): def test_user_can_use_device_now_no_group(self): self.assertFalse(self.noperm_userprofile.can_use_device_now(self.device)) + + def test_user_can_use_device_role_not_categories(self): + timeslot = TimeSlot.objects.create( + name="any day", + weekday_start=1, + weekday_end=7, + hour_start=dateparse.parse_time("00:00:00"), + hour_end=dateparse.parse_time("23:59:59"), + ) + + role = Role.objects.create( + name="role", + ) + role.time_slots.add(timeslot) + + group = Group.objects.create(name="group") + group.roles.add(role) + self.noperm_userprofile.groups.add(group) + self.assertFalse(self.noperm_userprofile.can_use_device_now(self.device))