mirror of
https://github.com/pretix/pretix.git
synced 2026-05-05 15:14:04 +00:00
Reusable Media: Mifare Ultralight AES support (#3335)
This commit is contained in:
@@ -223,6 +223,7 @@ class PretixPosSecurityProfile(AllowListSecurityProfile):
|
||||
('POST', 'api-v1:checkinrpc.redeem'),
|
||||
('GET', 'api-v1:checkinrpc.search'),
|
||||
('POST', 'api-v1:reusablemedium-lookup'),
|
||||
('POST', 'api-v1:reusablemedium-list'),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -817,6 +817,10 @@ class EventSettingsSerializer(SettingsSerializer):
|
||||
'reusable_media_type_nfc_uid',
|
||||
'reusable_media_type_nfc_uid_autocreate_giftcard',
|
||||
'reusable_media_type_nfc_uid_autocreate_giftcard_currency',
|
||||
'reusable_media_type_nfc_mf0aes',
|
||||
'reusable_media_type_nfc_mf0aes_autocreate_giftcard',
|
||||
'reusable_media_type_nfc_mf0aes_autocreate_giftcard_currency',
|
||||
'reusable_media_type_nfc_mf0aes_random_uid',
|
||||
]
|
||||
readonly_fields = [
|
||||
# These are read-only since they are currently only settable on organizers, not events
|
||||
@@ -826,6 +830,10 @@ class EventSettingsSerializer(SettingsSerializer):
|
||||
'reusable_media_type_nfc_uid',
|
||||
'reusable_media_type_nfc_uid_autocreate_giftcard',
|
||||
'reusable_media_type_nfc_uid_autocreate_giftcard_currency',
|
||||
'reusable_media_type_nfc_mf0aes',
|
||||
'reusable_media_type_nfc_mf0aes_autocreate_giftcard',
|
||||
'reusable_media_type_nfc_mf0aes_autocreate_giftcard_currency',
|
||||
'reusable_media_type_nfc_mf0aes_random_uid',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -894,6 +902,8 @@ class DeviceEventSettingsSerializer(EventSettingsSerializer):
|
||||
'name_scheme',
|
||||
'reusable_media_type_barcode',
|
||||
'reusable_media_type_nfc_uid',
|
||||
'reusable_media_type_nfc_mf0aes',
|
||||
'reusable_media_type_nfc_mf0aes_random_uid',
|
||||
'system_question_order',
|
||||
]
|
||||
|
||||
|
||||
@@ -392,6 +392,9 @@ class OrganizerSettingsSerializer(SettingsSerializer):
|
||||
'reusable_media_type_nfc_uid',
|
||||
'reusable_media_type_nfc_uid_autocreate_giftcard',
|
||||
'reusable_media_type_nfc_uid_autocreate_giftcard_currency',
|
||||
'reusable_media_type_nfc_mf0aes',
|
||||
'reusable_media_type_nfc_mf0aes_autocreate_giftcard',
|
||||
'reusable_media_type_nfc_mf0aes_autocreate_giftcard_currency',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
@@ -19,8 +19,12 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import base64
|
||||
import logging
|
||||
|
||||
from cryptography.hazmat.backends.openssl.backend import Backend
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
from cryptography.hazmat.primitives.serialization import load_pem_public_key
|
||||
from django.db.models import Exists, OuterRef, Q
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.utils.timezone import now
|
||||
@@ -34,6 +38,8 @@ from pretix.api.auth.device import DeviceTokenAuthentication
|
||||
from pretix.api.views.version import numeric_version
|
||||
from pretix.base.models import CheckinList, Device, SubEvent
|
||||
from pretix.base.models.devices import Gate, generate_api_token
|
||||
from pretix.base.models.media import MediumKeySet
|
||||
from pretix.base.services.media import get_keysets_for_organizer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -47,6 +53,17 @@ class InitializationRequestSerializer(serializers.Serializer):
|
||||
software_brand = serializers.CharField(max_length=190)
|
||||
software_version = serializers.CharField(max_length=190)
|
||||
info = serializers.JSONField(required=False, allow_null=True)
|
||||
rsa_pubkey = serializers.CharField(required=False, allow_null=True)
|
||||
|
||||
def validate(self, attrs):
|
||||
if attrs.get('rsa_pubkey'):
|
||||
try:
|
||||
load_pem_public_key(
|
||||
attrs['rsa_pubkey'].encode(), Backend()
|
||||
)
|
||||
except:
|
||||
raise ValidationError({'rsa_pubkey': ['Not a valid public key.']})
|
||||
return attrs
|
||||
|
||||
|
||||
class UpdateRequestSerializer(serializers.Serializer):
|
||||
@@ -57,6 +74,47 @@ class UpdateRequestSerializer(serializers.Serializer):
|
||||
software_brand = serializers.CharField(max_length=190)
|
||||
software_version = serializers.CharField(max_length=190)
|
||||
info = serializers.JSONField(required=False, allow_null=True)
|
||||
rsa_pubkey = serializers.CharField(required=False, allow_null=True)
|
||||
|
||||
def validate(self, attrs):
|
||||
if attrs.get('rsa_pubkey'):
|
||||
try:
|
||||
load_pem_public_key(
|
||||
attrs['rsa_pubkey'].encode(), Backend()
|
||||
)
|
||||
except:
|
||||
raise ValidationError({'rsa_pubkey': ['Not a valid public key.']})
|
||||
return attrs
|
||||
|
||||
|
||||
class RSAEncryptedField(serializers.Field):
|
||||
def to_representation(self, value):
|
||||
public_key = load_pem_public_key(
|
||||
self.context['device'].rsa_pubkey.encode(), Backend()
|
||||
)
|
||||
cipher_text = public_key.encrypt(
|
||||
# RSA/ECB/PKCS1Padding
|
||||
value,
|
||||
padding.PKCS1v15()
|
||||
)
|
||||
return base64.b64encode(cipher_text).decode()
|
||||
|
||||
|
||||
class MediumKeySetSerializer(serializers.ModelSerializer):
|
||||
uid_key = RSAEncryptedField(read_only=True)
|
||||
diversification_key = RSAEncryptedField(read_only=True)
|
||||
organizer = serializers.SlugRelatedField(slug_field='slug', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = MediumKeySet
|
||||
fields = [
|
||||
'public_id',
|
||||
'organizer',
|
||||
'active',
|
||||
'media_type',
|
||||
'uid_key',
|
||||
'diversification_key',
|
||||
]
|
||||
|
||||
|
||||
class GateSerializer(serializers.ModelSerializer):
|
||||
@@ -108,6 +166,8 @@ class InitializeView(APIView):
|
||||
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')
|
||||
print(serializer.validated_data, request.data)
|
||||
device.rsa_pubkey = serializer.validated_data.get('rsa_pubkey')
|
||||
device.api_token = generate_api_token()
|
||||
device.save()
|
||||
|
||||
@@ -130,6 +190,11 @@ class UpdateView(APIView):
|
||||
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')
|
||||
if serializer.validated_data.get('rsa_pubkey') and serializer.validated_data.get('rsa_pubkey') != device.rsa_pubkey:
|
||||
if device.rsa_pubkey:
|
||||
raise ValidationError({'rsa_pubkey': ['You cannot change the rsa_pubkey of the device once it is set.']})
|
||||
else:
|
||||
device.rsa_pubkey = serializer.validated_data.get('rsa_pubkey')
|
||||
device.info = serializer.validated_data.get('info')
|
||||
device.save()
|
||||
device.log_action('pretix.device.updated', data=serializer.validated_data, auth=device)
|
||||
@@ -177,8 +242,12 @@ class InfoView(APIView):
|
||||
'pretix': __version__,
|
||||
'pretix_numeric': numeric_version(__version__),
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
'medium_key_sets': MediumKeySetSerializer(
|
||||
get_keysets_for_organizer(device.organizer),
|
||||
many=True,
|
||||
context={'device': request.auth}
|
||||
).data if device.rsa_pubkey else []
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -104,6 +104,12 @@ class ReusableMediaViewSet(viewsets.ModelViewSet):
|
||||
auth=self.request.auth,
|
||||
data=merge_dicts(self.request.data, {'id': inst.pk})
|
||||
)
|
||||
mt = MEDIA_TYPES.get(serializer.validated_data["type"])
|
||||
if mt:
|
||||
m = mt.handle_new(self.request.organizer, inst, self.request.user, self.request.auth)
|
||||
if m:
|
||||
s = self.get_serializer(m)
|
||||
return Response({"result": s.data})
|
||||
|
||||
@transaction.atomic()
|
||||
def perform_update(self, serializer):
|
||||
|
||||
Reference in New Issue
Block a user