diff --git a/src/pretix/api/views/device.py b/src/pretix/api/views/device.py
index 7e717c23b..24c69ae61 100644
--- a/src/pretix/api/views/device.py
+++ b/src/pretix/api/views/device.py
@@ -93,6 +93,9 @@ class InitializeView(APIView):
if device.initialized:
raise ValidationError({'token': ['This initialization token has already been used.']})
+ if device.revoked:
+ raise ValidationError({'token': ['This initialization token has been revoked.']})
+
device.initialized = now()
device.hardware_brand = serializer.validated_data.get('hardware_brand')
device.hardware_model = serializer.validated_data.get('hardware_model')
diff --git a/src/pretix/control/templates/pretixcontrol/organizers/devices.html b/src/pretix/control/templates/pretixcontrol/organizers/devices.html
index 941683643..0274d2e4c 100644
--- a/src/pretix/control/templates/pretixcontrol/organizers/devices.html
+++ b/src/pretix/control/templates/pretixcontrol/organizers/devices.html
@@ -162,16 +162,19 @@
{% trans "Connect" %}
- {% elif d.api_token %}
+ {% endif %}
+ {% if not d.initialized or d.api_token %}
{% trans "Revoke access" %}
{% endif %}
-
-
- {% trans "Logs" %}
-
+ {% if d.initialized %}
+
+
+ {% trans "Logs" %}
+
+ {% endif %}
diff --git a/src/pretix/control/views/organizer.py b/src/pretix/control/views/organizer.py
index 239de7ee9..d824a6dab 100644
--- a/src/pretix/control/views/organizer.py
+++ b/src/pretix/control/views/organizer.py
@@ -1184,7 +1184,7 @@ class DeviceRevokeView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
def get(self, request, *args, **kwargs):
self.object = self.get_object()
- if not self.object.api_token:
+ if self.object.revoked:
messages.success(request, _('This device currently does not have access.'))
return redirect(reverse('control:organizer.devices', kwargs={
'organizer': self.request.organizer.slug,
diff --git a/src/tests/api/test_deviceauth.py b/src/tests/api/test_deviceauth.py
index 4fd9bd9bf..1e8d7017b 100644
--- a/src/tests/api/test_deviceauth.py
+++ b/src/tests/api/test_deviceauth.py
@@ -72,6 +72,21 @@ def test_initialize_used_token(client, device: Device):
assert resp.data == {'token': ['This initialization token has already been used.']}
+@pytest.mark.django_db
+def test_initialize_revoked_token(client, new_device: Device):
+ new_device.revoked = True
+ new_device.save()
+ resp = client.post('/api/v1/device/initialize', {
+ 'token': new_device.initialization_token,
+ 'hardware_brand': 'Samsung',
+ 'hardware_model': 'Galaxy S',
+ 'software_brand': 'pretixdroid',
+ 'software_version': '4.0.0'
+ })
+ assert resp.status_code == 400
+ assert resp.data == {'token': ['This initialization token has been revoked.']}
+
+
@pytest.mark.django_db
def test_initialize_valid_token(client, new_device: Device):
resp = client.post('/api/v1/device/initialize', {
diff --git a/src/tests/control/test_devices.py b/src/tests/control/test_devices.py
index 60e2d836e..d3a678410 100644
--- a/src/tests/control/test_devices.py
+++ b/src/tests/control/test_devices.py
@@ -110,6 +110,17 @@ def test_revoke_device(event, admin_user, admin_team, device, client):
assert device.revoked
+@pytest.mark.django_db
+def test_revoke_device_before_initialization(event, admin_user, admin_team, device, client):
+ client.login(email='dummy@dummy.dummy', password='dummy')
+ device.save()
+
+ client.get('/control/organizer/dummy/device/{}/revoke'.format(device.pk))
+ client.post('/control/organizer/dummy/device/{}/revoke'.format(device.pk), {}, follow=True)
+ device.refresh_from_db()
+ assert device.revoked
+
+
@pytest.mark.django_db
def test_bulk_update_device(event, admin_user, admin_team, device, client):
client.login(email='dummy@dummy.dummy', password='dummy')