From b83993cf1193da339a253108ac5b45cb232510a0 Mon Sep 17 00:00:00 2001 From: Ulhas Mandrawadkar Date: Wed, 6 Jan 2016 13:01:22 +0530 Subject: [PATCH] Adds support for large iOS device tokens - Changed max_length for registration_id to 200 from 64 - Added tests for 100 bytes device tokens - Changed Validation condition check in APNSDeviceSerializer - Changes to comments and readme - Added migrations --- README.rst | 2 +- push_notifications/api/rest_framework.py | 5 +++-- .../migrations/0002_auto_20160106_0850.py | 20 ++++++++++++++++++ push_notifications/models.py | 2 +- tests/test_rest_framework.py | 21 ++++++++++++++++--- 5 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 push_notifications/migrations/0002_auto_20160106_0850.py diff --git a/README.rst b/README.rst index bba7f7d4..1e6ca7cf 100644 --- a/README.rst +++ b/README.rst @@ -175,7 +175,7 @@ ViewSets are available for both APNS and GCM devices in two permission flavors: - Permissions are ``IsAuthenticated`` and custom permission ``IsOwner``, which will only allow the ``request.user`` to get and update devices that belong to that user - Requires a user to be authenticated, so all devices will be associated with a user -When creating an ``APNSDevice``, the ``registration_id`` is validated to be a 64-character hexadecimal string. +When creating an ``APNSDevice``, the ``registration_id`` is validated to be a 64-character or 200-character hexadecimal string. Since 2016, device tokens are to be increased from 32 bytes to 100 bytes. Routes can be added one of two ways: diff --git a/push_notifications/api/rest_framework.py b/push_notifications/api/rest_framework.py index b5317438..7f0b826c 100644 --- a/push_notifications/api/rest_framework.py +++ b/push_notifications/api/rest_framework.py @@ -47,9 +47,10 @@ class Meta(DeviceSerializerMixin.Meta): model = APNSDevice def validate_registration_id(self, value): - # iOS device tokens are 256-bit hexadecimal (64 characters) + # iOS device tokens are 256-bit hexadecimal (64 characters). In 2016 Apple is increasing + # iOS device tokens to 100 bytes hexadecimal (200 characters). - if hex_re.match(value) is None or len(value) != 64: + if hex_re.match(value) is None or len(value) not in (64, 200): raise ValidationError("Registration ID (device token) is invalid") return value diff --git a/push_notifications/migrations/0002_auto_20160106_0850.py b/push_notifications/migrations/0002_auto_20160106_0850.py new file mode 100644 index 00000000..d7b9d759 --- /dev/null +++ b/push_notifications/migrations/0002_auto_20160106_0850.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.1 on 2016-01-06 08:50 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('push_notifications', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='apnsdevice', + name='registration_id', + field=models.CharField(max_length=200, unique=True, verbose_name='Registration ID'), + ), + ] diff --git a/push_notifications/models.py b/push_notifications/models.py index 43a7ffd7..4a6bdfd0 100644 --- a/push_notifications/models.py +++ b/push_notifications/models.py @@ -80,7 +80,7 @@ def send_message(self, message, **kwargs): class APNSDevice(Device): device_id = models.UUIDField(verbose_name=_("Device ID"), blank=True, null=True, db_index=True, help_text="UDID / UIDevice.identifierForVendor()") - registration_id = models.CharField(verbose_name=_("Registration ID"), max_length=64, unique=True) + registration_id = models.CharField(verbose_name=_("Registration ID"), max_length=200, unique=True) objects = APNSDeviceManager() diff --git a/tests/test_rest_framework.py b/tests/test_rest_framework.py index 832ee0c1..8e5b4862 100644 --- a/tests/test_rest_framework.py +++ b/tests/test_rest_framework.py @@ -6,7 +6,7 @@ class APNSDeviceSerializerTestCase(TestCase): def test_validation(self): - # valid data - upper case + # valid data - 32 bytes upper case serializer = APNSDeviceSerializer(data={ "registration_id": "AEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAE", "name": "Apple iPhone 6+", @@ -14,7 +14,7 @@ def test_validation(self): }) self.assertTrue(serializer.is_valid()) - # valid data - lower case + # valid data - 32 bytes lower case serializer = APNSDeviceSerializer(data={ "registration_id": "aeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeae", "name": "Apple iPhone 6+", @@ -22,6 +22,22 @@ def test_validation(self): }) self.assertTrue(serializer.is_valid()) + # valid data - 100 bytes upper case + serializer = APNSDeviceSerializer(data={ + "registration_id": "AEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAE", + "name": "Apple iPhone 6+", + "device_id": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + }) + self.assertTrue(serializer.is_valid()) + + # valid data - 100 bytes lower case + serializer = APNSDeviceSerializer(data={ + "registration_id": "aeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeae", + "name": "Apple iPhone 6+", + "device_id": "ffffffffffffffffffffffffffffffff", + }) + self.assertTrue(serializer.is_valid()) + # invalid data - device_id, registration_id serializer = APNSDeviceSerializer(data={ "registration_id": "invalid device token contains no hex", @@ -102,4 +118,3 @@ def test_device_id_validation_value_between_signed_unsigned_64b_int_maximums(sel "device_id": "e87a4e72d634997c", }) self.assertTrue(serializer.is_valid()) -