enforce size limitation control (#15350)
This commit is contained in:
parent
6378c7f549
commit
1d35d73c7f
|
@ -24,7 +24,7 @@ from rest_framework.views import APIView
|
|||
from rest_framework.response import Response
|
||||
|
||||
from .models import CUTIdentifier, Petal, Partner
|
||||
from .utils import get_petal_object_or_404, logit
|
||||
from .utils import get_petal_object_or_404, logit, is_within_size_limits, notify_admins
|
||||
from .exceptions import PetalException
|
||||
from .renderers import PetalRenderer
|
||||
|
||||
|
@ -99,15 +99,28 @@ class PetalAPIView(APIView):
|
|||
return Response({"error": "content-type-not-found", "description": "Header Content-Type not found"},
|
||||
status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# checking size limitation
|
||||
content_length = len(data)
|
||||
partner_current_size = Petal.objects.filter(partner_id=self.id_partner).aggregate(Sum('size')).values()[0]
|
||||
if partner_current_size is None:
|
||||
partner_current_size = 0
|
||||
if partner_current_size + content_length > self.id_partner.max_size:
|
||||
# global size limits control
|
||||
partner_current_size = Petal.objects.filter(partner_id=self.id_partner).aggregate(Sum('size')).values()[0] or 0
|
||||
hard, soft = is_within_size_limits(partner_current_size + content_length, self.id_partner.hard_global_max_size,
|
||||
self.id_partner.soft_global_max_size)
|
||||
if not hard:
|
||||
return Response({"error": "global-space-exhausted",
|
||||
"description": "no more space left for %s" % self.id_partner.name},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
if hard and not soft:
|
||||
notify_admins(self.id_partner.name, self.id_partner.admin_emails, partner_current_size + content_length,
|
||||
self.id_partner.hard_global_max_size)
|
||||
# key size limits control
|
||||
hard, soft = is_within_size_limits(content_length, self.id_partner.hard_per_key_max_size,
|
||||
self.id_partner.soft_per_key_max_size)
|
||||
if not hard:
|
||||
return Response({"error": "data-size-error",
|
||||
"description": "%s data size(%d) over limit(%d)" % (self.id_key, content_length, self.id_partner.hard_per_key_max_size)},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
if hard and not soft:
|
||||
notify_admins(self.id_partner.name, self.id_partner.admin_emails, content_length,
|
||||
self.id_partner.hard_per_key_max_size, self.id_key)
|
||||
|
||||
def is_precondition_ok(etags):
|
||||
if etags == '*':
|
||||
|
|
|
@ -24,13 +24,19 @@ from django.core.exceptions import ValidationError
|
|||
|
||||
|
||||
class Partner(models.Model):
|
||||
name = models.CharField(_('Partner'), max_length=64,
|
||||
name = models.CharField(verbose_name=_('Partner'), max_length=64,
|
||||
validators=[RegexValidator('^[A-Za-z0-9-_]+$', message="Invalid name format")])
|
||||
max_size = models.IntegerField(_('Max Size'))
|
||||
min_size = models.IntegerField(_('Min Size'))
|
||||
admin_emails = models.CharField(verbose_name=_('Admin emails'), max_length=256,
|
||||
help_text=_('List of admin emails separated by comma'))
|
||||
hard_global_max_size = models.IntegerField(verbose_name=_('Hard max size'))
|
||||
soft_global_max_size = models.IntegerField(_('Soft max size'))
|
||||
hard_per_key_max_size = models.IntegerField(_('Hard max size per key'))
|
||||
soft_per_key_max_size = models.IntegerField(_('Soft max size per key'))
|
||||
|
||||
def __unicode__(self):
|
||||
return '%s %d %d' % (self.name, self.max_size, self.min_size)
|
||||
return '%s - %s - (hg:%d, sg:%d, hk:%d, sk:%d)' % (self.name, self.admin_emails, self.hard_global_max_size,
|
||||
self.soft_global_max_size, self.hard_per_key_max_size,
|
||||
self.soft_per_key_max_size)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.full_clean()
|
||||
|
|
|
@ -18,6 +18,9 @@ import logging
|
|||
from functools import wraps
|
||||
from petale.exceptions import PetalException
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.mail import send_mail
|
||||
|
||||
|
||||
def get_petal_object_or_404(model, **kwargs):
|
||||
try:
|
||||
|
@ -28,6 +31,26 @@ def get_petal_object_or_404(model, **kwargs):
|
|||
'description': '%s %s does not exist' % (model.__name__, kwargs.values()[0])})
|
||||
|
||||
|
||||
def is_within_size_limits(size, gmax, smax=None):
|
||||
if size > gmax:
|
||||
return (False, False)
|
||||
if smax and gmax > size > smax:
|
||||
return (True, False)
|
||||
return (True, True)
|
||||
|
||||
|
||||
def notify_admins(partner, recipients, size, hmax, key=None):
|
||||
if key:
|
||||
subject = 'Key %s space almost exhausted' % key
|
||||
else:
|
||||
subject = 'Partner %s space almost exhausted' % partner
|
||||
|
||||
body = 'Current size: %s, Max size: %s' % (size, hmax)
|
||||
sender = settings.DEFAULT_FROM_EMAIL
|
||||
recipients = recipients.split(',')
|
||||
send_mail(subject, body, sender, recipients)
|
||||
|
||||
|
||||
def logit(func):
|
||||
@wraps(func)
|
||||
def wrapper(self, request, *args, **kwargs):
|
||||
|
|
|
@ -35,13 +35,13 @@ def service_cityhall(db):
|
|||
|
||||
@pytest.fixture
|
||||
def partner_southpark(service_family, service_library, service_cityhall):
|
||||
return create_partner('southpark', min_size=512, max_size=20240)
|
||||
return create_partner('southpark', hg=20240, hk=19728)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def partner_gotham():
|
||||
create_service('arkham')
|
||||
return create_partner('gotham', min_size=256, max_size=2048)
|
||||
return create_partner('gotham', admins='b.wayne@gotham.gov', hg=2048, sg=1600, hk=400, sk=370)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"due_date": "11/12/2016",
|
||||
"first_name": "loking",
|
||||
"last_name": "josh",
|
||||
"address": "274 West 12th Avenue, Vancouver, BC V5Y 1V4",
|
||||
"rates": [
|
||||
{
|
||||
"name": "WA CITY TAX",
|
||||
"rate": 0.021,
|
||||
"type": "city"
|
||||
},
|
||||
{
|
||||
"name": "WA STATE TAX",
|
||||
"rate": 0.065,
|
||||
"type": "state"
|
||||
}
|
||||
],
|
||||
"taxe_no": "1234",
|
||||
"totalRate": 0.086,
|
||||
"total_due": 1290.0,
|
||||
"total_income": 15000
|
||||
}
|
|
@ -219,14 +219,39 @@ def test_caching(app, partner_southpark, cut_kevin, acl):
|
|||
resp = app.put_json(url, params=payload, headers={'If-Match': etag}, status=200)
|
||||
|
||||
|
||||
def test_partner_size_limit(app, cut_kevin, acl, petal_invoice, petal_books):
|
||||
def test_partner_size_limit(app, cut_kevin, acl, petal_invoice, petal_books, mailoutbox):
|
||||
app.authorization = ('Basic', ('arkham', 'arkham'))
|
||||
payload = json.loads(get_tests_file_content('books.json'))
|
||||
payload = json.loads(get_tests_file_content('taxe.json'))
|
||||
|
||||
for i in range(3):
|
||||
# test sending data sized above key limits
|
||||
payload['phonenumber'] = '+18855776644'
|
||||
payload['birthdate'] = '1980/06/21'
|
||||
payload['birthplace'] = 'Chelsea, Quebec'
|
||||
url = '/api/gotham/%s/taxes-fail/' % cut_kevin.uuid
|
||||
resp = app.put_json(url, params=payload, headers={'If-None-Match': '*'}, status=500)
|
||||
assert resp.json['error'] == 'data-size-error'
|
||||
assert resp.json['description'] == 'taxes-fail data size(428) over limit(400)'
|
||||
|
||||
# test sending data sized within soft and hard key limit
|
||||
payload.pop('birthplace')
|
||||
resp = app.put_json(url, params=payload, headers={'If-None-Match': '*'}, status=201)
|
||||
assert len(mailoutbox) == 1
|
||||
sent_mail = mailoutbox[0]
|
||||
assert sent_mail.to[0] == 'b.wayne@gotham.gov'
|
||||
assert sent_mail.subject == 'Key taxes-fail space almost exhausted'
|
||||
assert 'Current size: 395, Max size: 400' in sent_mail.message().as_string()
|
||||
|
||||
payload.pop('birthdate')
|
||||
for i in range(4):
|
||||
url = '/api/gotham/%s/taxes-%d/' % (cut_kevin.uuid, i)
|
||||
app.put_json(url, params=payload, headers={'If-None-Match': '*'}, status=201)
|
||||
|
||||
assert len(mailoutbox) == 2
|
||||
sent_mail = mailoutbox[1]
|
||||
assert sent_mail.to[0] == 'b.wayne@gotham.gov'
|
||||
assert sent_mail.subject == 'Partner gotham space almost exhausted'
|
||||
assert 'Current size: 1867, Max size: 2048' in sent_mail.message().as_string()
|
||||
|
||||
url = '/api/gotham/%s/taxes-4/' % cut_kevin.uuid
|
||||
resp = app.put_json(url, params=payload, headers={'If-None-Match': '*'}, status=500)
|
||||
assert resp.json['error'] == 'global-space-exhausted'
|
||||
|
|
|
@ -35,8 +35,13 @@ def get_service(name):
|
|||
return User.objects.get(username=name)
|
||||
|
||||
|
||||
def create_partner(name, min_size=0, max_size=128):
|
||||
return Partner.objects.create(name=name, max_size=max_size, min_size=min_size)
|
||||
def create_partner(name, admins=None, hg=2048, sg=1536, hk=1024, sk=768):
|
||||
if not admins:
|
||||
admins = 'e.cartman@southpark.com,t.blakc@southpark.com'
|
||||
return Partner.objects.create(
|
||||
name=name, admin_emails=admins,
|
||||
hard_global_max_size=hg, soft_global_max_size=sg,
|
||||
hard_per_key_max_size=hk, soft_per_key_max_size=sk)
|
||||
|
||||
|
||||
def create_acl_record(order, partner, user, key, methods='*'):
|
||||
|
|
Loading…
Reference in New Issue