qrcode: add qrcode reader management endpoints (#82650)
gitea/passerelle/pipeline/head This commit looks good
Details
gitea/passerelle/pipeline/head This commit looks good
Details
This commit is contained in:
parent
a51c49a865
commit
82e9018865
|
@ -0,0 +1,35 @@
|
|||
# Generated by Django 3.2.18 on 2023-11-02 09:31
|
||||
|
||||
import uuid
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('qrcode', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Reader',
|
||||
fields=[
|
||||
(
|
||||
'id',
|
||||
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||
),
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, unique=True, verbose_name='UUID')),
|
||||
('validity_start', models.DateTimeField(verbose_name='Validity Start Date')),
|
||||
('validity_end', models.DateTimeField(verbose_name='Validity End Date')),
|
||||
(
|
||||
'resource',
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name='readers',
|
||||
to='qrcode.qrcodeconnector',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -1,12 +1,14 @@
|
|||
import binascii
|
||||
import os
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from io import BytesIO
|
||||
|
||||
from django.core.validators import RegexValidator
|
||||
from django.db import models
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.template.response import TemplateResponse
|
||||
from django.urls import reverse
|
||||
from django.utils.dateparse import parse_datetime
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
@ -33,6 +35,16 @@ CERTIFICATE_SCHEMA = {
|
|||
},
|
||||
}
|
||||
|
||||
READER_SCHEMA = {
|
||||
'$schema': 'http://json-schema.org/draft-04/schema#',
|
||||
'type': 'object',
|
||||
'additionalProperties': False,
|
||||
'properties': {
|
||||
'validity_start': {'type': 'string', 'format': 'date-time'},
|
||||
'validity_end': {'type': 'string', 'format': 'date-time'},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def generate_key():
|
||||
key = os.urandom(32)
|
||||
|
@ -135,6 +147,90 @@ class QRCodeConnector(BaseResource):
|
|||
qr_code = certificate.generate_qr_code()
|
||||
return HttpResponse(qr_code, content_type='image/png')
|
||||
|
||||
@endpoint(
|
||||
name='save-reader',
|
||||
pattern=f'^{UUID_PATTERN}?$',
|
||||
example_pattern='{uuid}',
|
||||
description=_('Create or update a qrcode reader'),
|
||||
post={'request_body': {'schema': {'application/json': READER_SCHEMA}}},
|
||||
parameters={
|
||||
'uuid': {
|
||||
'description': _('QRCode reader identifier'),
|
||||
'example_value': '12345678-1234-1234-1234-123456789012',
|
||||
}
|
||||
},
|
||||
)
|
||||
def save_reader(self, request, uuid=None, post_data=None):
|
||||
validity_start = parse_datetime(post_data['validity_start'])
|
||||
validity_end = parse_datetime(post_data['validity_end'])
|
||||
|
||||
if not uuid:
|
||||
reader = self.readers.create(
|
||||
validity_start=validity_start,
|
||||
validity_end=validity_end,
|
||||
)
|
||||
else:
|
||||
reader = get_object_or_404(self.readers, uuid=uuid)
|
||||
reader.validity_start = validity_start
|
||||
reader.validity_end = validity_end
|
||||
reader.save()
|
||||
|
||||
return {
|
||||
'data': {
|
||||
'uuid': reader.uuid,
|
||||
'url': reader.get_url(request),
|
||||
}
|
||||
}
|
||||
|
||||
@endpoint(
|
||||
name='get-reader',
|
||||
description=_('Get informations about a QRCode reader'),
|
||||
pattern=f'^{UUID_PATTERN}$',
|
||||
example_pattern='{uuid}',
|
||||
parameters={
|
||||
'uuid': {
|
||||
'description': _('QRCode reader identifier'),
|
||||
'example_value': '12345678-1234-1234-1234-123456789012',
|
||||
}
|
||||
},
|
||||
)
|
||||
def get_reader(self, request, uuid):
|
||||
reader = get_object_or_404(self.readers, uuid=uuid)
|
||||
return {
|
||||
'err': 0,
|
||||
'data': {
|
||||
'uuid': reader.uuid,
|
||||
'validity_start': reader.validity_start.isoformat(),
|
||||
'validity_end': reader.validity_end.isoformat(),
|
||||
'url': reader.get_url(request),
|
||||
},
|
||||
}
|
||||
|
||||
@endpoint(
|
||||
name='open-reader',
|
||||
description=_('Open a QRCode reader page.'),
|
||||
pattern=f'^{UUID_PATTERN}$',
|
||||
example_pattern='{uuid}',
|
||||
parameters={
|
||||
'uuid': {
|
||||
'description': _('QRCode reader identifier'),
|
||||
'example_value': '12345678-1234-1234-1234-123456789012',
|
||||
}
|
||||
},
|
||||
)
|
||||
def open_reader(self, request, uuid):
|
||||
reader = get_object_or_404(self.readers, uuid=uuid)
|
||||
now = datetime.now(timezone.utc)
|
||||
return TemplateResponse(
|
||||
request,
|
||||
'qrcode/qrcode-reader.html',
|
||||
context={
|
||||
'started': now >= reader.validity_start,
|
||||
'expired': now >= reader.validity_end,
|
||||
'reader': reader,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def encode_mime_like(data):
|
||||
msg = ''
|
||||
|
@ -198,3 +294,22 @@ class Certificate(models.Model):
|
|||
},
|
||||
)
|
||||
return request.build_absolute_uri(qrcode_relative_url)
|
||||
|
||||
|
||||
class Reader(models.Model):
|
||||
uuid = models.UUIDField(verbose_name=_('UUID'), unique=True, default=uuid.uuid4)
|
||||
validity_start = models.DateTimeField(verbose_name=_('Validity Start Date'))
|
||||
validity_end = models.DateTimeField(verbose_name=_('Validity End Date'))
|
||||
resource = models.ForeignKey(QRCodeConnector, on_delete=models.CASCADE, related_name='readers')
|
||||
|
||||
def get_url(self, request):
|
||||
relative_url = reverse(
|
||||
'generic-endpoint',
|
||||
kwargs={
|
||||
'slug': self.resource.slug,
|
||||
'connector': self.resource.get_connector_slug(),
|
||||
'endpoint': 'open-reader',
|
||||
'rest': str(self.uuid),
|
||||
},
|
||||
)
|
||||
return request.build_absolute_uri(relative_url)
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
{% load i18n %}
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<style>
|
||||
</style>
|
||||
<body>
|
||||
{% if not started %}
|
||||
{% trans "Reader isn't usable yet." %}
|
||||
{% elif expired %}
|
||||
{% trans "Reader has expired." %}
|
||||
{% else %}
|
||||
{{ reader.validity_start }}<br />
|
||||
{{ now }}
|
||||
{{ reader.uuid }}
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
|
@ -120,3 +120,79 @@ def test_get_qrcode(app, connector):
|
|||
with open('tests/data/qrcode/test-qrcode.png', 'rb') as expected_qrcode:
|
||||
# just check images are the same. Decoded content is tested javascript-side.
|
||||
assert response.body == expected_qrcode.read()
|
||||
|
||||
|
||||
def test_save_reader(app, connector):
|
||||
endpoint = generic_endpoint_url('qrcode', 'save-reader', slug=connector.slug)
|
||||
|
||||
result = app.post_json(
|
||||
endpoint,
|
||||
params={
|
||||
'validity_start': '2022-01-01 10:00:00+00:00',
|
||||
'validity_end': '2023-01-01 10:00:00+00:00',
|
||||
},
|
||||
)
|
||||
|
||||
assert result.json['err'] == 0
|
||||
|
||||
reader_uuid = result.json['data']['uuid']
|
||||
assert result.json['data']['url'] == f'http://testserver/qrcode/test/open-reader/{reader_uuid}'
|
||||
reader = connector.readers.get(uuid=reader_uuid)
|
||||
|
||||
assert reader.validity_start == datetime.datetime(2022, 1, 1, 10, 0, 0, 0, tzinfo=timezone.utc)
|
||||
assert reader.validity_end == datetime.datetime(2023, 1, 1, 10, 0, 0, 0, tzinfo=timezone.utc)
|
||||
|
||||
result = app.post_json(
|
||||
f'{endpoint}/{reader_uuid}',
|
||||
params={
|
||||
'validity_start': '2024-01-01T10:00:00+00:00',
|
||||
'validity_end': '2025-01-01T10:00:00+00:00',
|
||||
},
|
||||
)
|
||||
|
||||
reader.refresh_from_db()
|
||||
assert reader.validity_start == datetime.datetime(2024, 1, 1, 10, 0, 0, 0, tzinfo=timezone.utc)
|
||||
assert reader.validity_end == datetime.datetime(2025, 1, 1, 10, 0, 0, 0, tzinfo=timezone.utc)
|
||||
|
||||
|
||||
def test_get_reader(app, connector):
|
||||
reader = connector.readers.create(
|
||||
validity_start=datetime.datetime(2022, 1, 1, 10, 0, 0, 0, tzinfo=timezone.utc),
|
||||
validity_end=datetime.datetime(2023, 1, 1, 10, 0, 0, 0, tzinfo=timezone.utc),
|
||||
)
|
||||
|
||||
endpoint = generic_endpoint_url('qrcode', 'get-reader', slug=connector.slug)
|
||||
result = app.get(f'{endpoint}/{reader.uuid}')
|
||||
|
||||
assert result.json == {
|
||||
'err': 0,
|
||||
'data': {
|
||||
'uuid': str(reader.uuid),
|
||||
'validity_start': '2022-01-01T10:00:00+00:00',
|
||||
'validity_end': '2023-01-01T10:00:00+00:00',
|
||||
'url': f'http://testserver/qrcode/test/open-reader/{reader.uuid}',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_open_reader(app, connector, freezer):
|
||||
reader = connector.readers.create(
|
||||
validity_start=datetime.datetime(2022, 1, 1, 10, 0, 0, 0, tzinfo=timezone.utc),
|
||||
validity_end=datetime.datetime(2023, 1, 1, 10, 0, 0, 0, tzinfo=timezone.utc),
|
||||
)
|
||||
|
||||
endpoint = generic_endpoint_url('qrcode', 'open-reader', slug=connector.slug)
|
||||
freezer.move_to('2022-01-01T09:59:59')
|
||||
result = app.get(f'{endpoint}/{reader.uuid}')
|
||||
|
||||
assert 'Reader isn\'t usable yet' in result.body.decode('utf-8')
|
||||
|
||||
freezer.move_to('2022-01-01T10:00:00')
|
||||
result = app.get(f'{endpoint}/{reader.uuid}')
|
||||
|
||||
assert str(reader.uuid) in result.body.decode('utf-8')
|
||||
|
||||
freezer.move_to('2023-01-01T10:00:01')
|
||||
result = app.get(f'{endpoint}/{reader.uuid}')
|
||||
|
||||
assert 'Reader has expired.' in result.body.decode('utf-8')
|
||||
|
|
Loading…
Reference in New Issue