diff --git a/doc/api/deviceauth.rst b/doc/api/deviceauth.rst index 1d946860c2..0e5433daea 100644 --- a/doc/api/deviceauth.rst +++ b/doc/api/deviceauth.rst @@ -32,6 +32,8 @@ as well as the type of underlying hardware. Example: "token": "kpp4jn8g2ynzonp6", "hardware_brand": "Samsung", "hardware_model": "Galaxy S", + "os_name": "Android", + "os_version": "2.3.6", "software_brand": "pretixdroid", "software_version": "4.0.0" } @@ -98,6 +100,8 @@ following endpoint: { "hardware_brand": "Samsung", "hardware_model": "Galaxy S", + "os_name": "Android", + "os_version": "2.3.6", "software_brand": "pretixdroid", "software_version": "4.1.0", "info": {"arbitrary": "data"} diff --git a/doc/api/resources/devices.rst b/doc/api/resources/devices.rst index 08975bbe1f..85d507f223 100644 --- a/doc/api/resources/devices.rst +++ b/doc/api/resources/devices.rst @@ -24,6 +24,8 @@ all_events boolean Whether this de limit_events list List of event slugs this device has access to hardware_brand string Device hardware manufacturer (read-only) hardware_model string Device hardware model (read-only) +os_name string Device operating system name (read-only) +os_version string Device operating system version (read-only) software_brand string Device software product (read-only) software_version string Device software version (read-only) created datetime Creation time @@ -76,6 +78,8 @@ Device endpoints "security_profile": "full", "hardware_brand": "Zebra", "hardware_model": "TC25", + "os_name": "Android", + "os_version": "8.1.0", "software_brand": "pretixSCAN", "software_version": "1.5.1" } @@ -123,6 +127,8 @@ Device endpoints "security_profile": "full", "hardware_brand": "Zebra", "hardware_model": "TC25", + "os_name": "Android", + "os_version": "8.1.0", "software_brand": "pretixSCAN", "software_version": "1.5.1" } @@ -173,6 +179,8 @@ Device endpoints "initialized": null "hardware_brand": null, "hardware_model": null, + "os_name": null, + "os_version": null, "software_brand": null, "software_version": null } diff --git a/src/pretix/api/serializers/organizer.py b/src/pretix/api/serializers/organizer.py index 696646b033..325c468d52 100644 --- a/src/pretix/api/serializers/organizer.py +++ b/src/pretix/api/serializers/organizer.py @@ -251,6 +251,8 @@ class DeviceSerializer(serializers.ModelSerializer): unique_serial = serializers.CharField(read_only=True) hardware_brand = serializers.CharField(read_only=True) hardware_model = serializers.CharField(read_only=True) + os_name = serializers.CharField(read_only=True) + os_version = serializers.CharField(read_only=True) software_brand = serializers.CharField(read_only=True) software_version = serializers.CharField(read_only=True) created = serializers.DateTimeField(read_only=True) @@ -263,7 +265,7 @@ class DeviceSerializer(serializers.ModelSerializer): fields = ( 'device_id', 'unique_serial', 'initialization_token', 'all_events', 'limit_events', 'revoked', 'name', 'created', 'initialized', 'hardware_brand', 'hardware_model', - 'software_brand', 'software_version', 'security_profile' + 'os_name', 'os_version', 'software_brand', 'software_version', 'security_profile' ) diff --git a/src/pretix/api/views/device.py b/src/pretix/api/views/device.py index 24c69ae617..4fee965d32 100644 --- a/src/pretix/api/views/device.py +++ b/src/pretix/api/views/device.py @@ -42,6 +42,8 @@ class InitializationRequestSerializer(serializers.Serializer): token = serializers.CharField(max_length=190) hardware_brand = serializers.CharField(max_length=190) hardware_model = serializers.CharField(max_length=190) + os_name = serializers.CharField(max_length=190, required=False, allow_null=True) + os_version = serializers.CharField(max_length=190, required=False, allow_null=True) software_brand = serializers.CharField(max_length=190) software_version = serializers.CharField(max_length=190) info = serializers.JSONField(required=False, allow_null=True) @@ -50,6 +52,8 @@ class InitializationRequestSerializer(serializers.Serializer): class UpdateRequestSerializer(serializers.Serializer): hardware_brand = serializers.CharField(max_length=190) hardware_model = serializers.CharField(max_length=190) + os_name = serializers.CharField(max_length=190, required=False, allow_null=True) + os_version = serializers.CharField(max_length=190, required=False, allow_null=True) software_brand = serializers.CharField(max_length=190) software_version = serializers.CharField(max_length=190) info = serializers.JSONField(required=False, allow_null=True) @@ -99,6 +103,8 @@ class InitializeView(APIView): device.initialized = now() device.hardware_brand = serializer.validated_data.get('hardware_brand') device.hardware_model = serializer.validated_data.get('hardware_model') + device.os_name = serializer.validated_data.get('os_name') + device.os_version = serializer.validated_data.get('os_version') device.software_brand = serializer.validated_data.get('software_brand') device.software_version = serializer.validated_data.get('software_version') device.info = serializer.validated_data.get('info') @@ -120,6 +126,8 @@ class UpdateView(APIView): device = request.auth device.hardware_brand = serializer.validated_data.get('hardware_brand') device.hardware_model = serializer.validated_data.get('hardware_model') + device.os_name = serializer.validated_data.get('os_name') + device.os_version = serializer.validated_data.get('os_version') device.software_brand = serializer.validated_data.get('software_brand') device.software_version = serializer.validated_data.get('software_version') device.info = serializer.validated_data.get('info') diff --git a/src/pretix/base/migrations/0243_device_os_name_and_os_version.py b/src/pretix/base/migrations/0243_device_os_name_and_os_version.py new file mode 100644 index 0000000000..c9f9e29071 --- /dev/null +++ b/src/pretix/base/migrations/0243_device_os_name_and_os_version.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.9 on 2023-06-26 10:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0242_auto_20230512_1008'), + ] + + operations = [ + migrations.AddField( + model_name='device', + name='os_name', + field=models.CharField(max_length=190, null=True), + ), + migrations.AddField( + model_name='device', + name='os_version', + field=models.CharField(max_length=190, null=True), + ), + ] diff --git a/src/pretix/base/models/devices.py b/src/pretix/base/models/devices.py index 858ef5906e..f72ca0a38a 100644 --- a/src/pretix/base/models/devices.py +++ b/src/pretix/base/models/devices.py @@ -143,6 +143,14 @@ class Device(LoggedModel): max_length=190, null=True, blank=True ) + os_name = models.CharField( + max_length=190, + null=True, blank=True + ) + os_version = models.CharField( + max_length=190, + null=True, blank=True + ) software_brand = models.CharField( max_length=190, null=True, blank=True diff --git a/src/tests/api/test_deviceauth.py b/src/tests/api/test_deviceauth.py index 1e8d7017b4..04e88d7d24 100644 --- a/src/tests/api/test_deviceauth.py +++ b/src/tests/api/test_deviceauth.py @@ -94,6 +94,8 @@ def test_initialize_valid_token(client, new_device: Device): 'hardware_brand': 'Samsung', 'hardware_model': 'Galaxy S', 'software_brand': 'pretixdroid', + 'os_name': 'Android', + 'os_version': '2.3.3', 'software_version': '4.0.0' }) assert resp.status_code == 200 @@ -105,6 +107,7 @@ def test_initialize_valid_token(client, new_device: Device): new_device.refresh_from_db() assert new_device.api_token assert new_device.initialized + assert new_device.os_version == "2.3.3" @pytest.mark.django_db @@ -142,6 +145,8 @@ def test_update_valid_fields(device_client, device: Device): resp = device_client.post('/api/v1/device/update', { 'hardware_brand': 'Samsung', 'hardware_model': 'Galaxy S', + 'os_name': 'Android', + 'os_version': '2.3.3', 'software_brand': 'pretixdroid', 'software_version': '5.0.0', 'info': { @@ -151,9 +156,23 @@ def test_update_valid_fields(device_client, device: Device): assert resp.status_code == 200 device.refresh_from_db() assert device.software_version == '5.0.0' + assert device.os_version == '2.3.3' assert device.info == {'foo': 'bar'} +@pytest.mark.django_db +def test_update_valid_without_optional_fields(device_client, device: Device): + resp = device_client.post('/api/v1/device/update', { + 'hardware_brand': 'Samsung', + 'hardware_model': 'Galaxy S', + 'software_brand': 'pretixdroid', + 'software_version': '5.0.0', + }, format='json') + assert resp.status_code == 200 + device.refresh_from_db() + assert device.software_version == '5.0.0' + + @pytest.mark.django_db def test_keyroll_required_auth(client, token_client, device: Device): resp = client.post('/api/v1/device/roll', {}) diff --git a/src/tests/api/test_devices.py b/src/tests/api/test_devices.py index 22e176b54f..e1856ad062 100644 --- a/src/tests/api/test_devices.py +++ b/src/tests/api/test_devices.py @@ -35,6 +35,8 @@ def device(organizer, event): unique_serial="UOS3GNZ27O39V3QS", initialization_token="frkso3m2w58zuw70", hardware_model="TC25", + os_name="Android", + os_version="8.1.0", software_brand="pretixSCAN", software_version="1.5.1", initialized=now(), @@ -58,6 +60,8 @@ TEST_DEV_RES = { "initialized": "2020-09-18T14:17:44.190021Z", "hardware_brand": "Zebra", "hardware_model": "TC25", + "os_name": "Android", + "os_version": "8.1.0", "software_brand": "pretixSCAN", "software_version": "1.5.1", "security_profile": "full"