Projet

Général

Profil

0001-utils-use-pycrytodomex-replace-Crypto-with-Cryptodom.patch

Nicolas Roche, 03 juin 2020 14:40

Télécharger (9,89 ko)

Voir les différences:

Subject: [PATCH] utils: use pycrytodomex, replace Crypto with Cryptodome
 (#43563)

 combo/apps/lingo/models.py       |  4 ++--
 combo/utils/crypto.py            |  6 +++---
 setup.py                         |  1 +
 tests/test_lingo_remote_regie.py | 10 +++++-----
 tests/test_utils.py              |  3 ++-
 5 files changed, 13 insertions(+), 11 deletions(-)
combo/apps/lingo/models.py
30 30
from django.conf import settings
31 31
from django.db import models
32 32
from django.forms import models as model_forms, Select
33 33
from django.utils.translation import ugettext_lazy as _
34 34
from django.utils import timezone, dateparse, six
35 35
from django.core.mail import EmailMultiAlternatives
36 36
from django.urls import reverse
37 37
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied, ValidationError
38
from django.utils.encoding import python_2_unicode_compatible
38
from django.utils.encoding import force_bytes, python_2_unicode_compatible
39 39
from django.utils.formats import localize
40 40
from django.utils.http import urlencode
41 41
from django.utils.six.moves.urllib import parse as urlparse
42 42
from django.utils.timezone import make_aware, utc
43 43

  
44 44
from django.contrib.auth.models import User
45 45
from django.template.loader import render_to_string
46 46

  
......
454 454
                   'autobilling': _('Autobilling has been set for this invoice.'),
455 455
                   'past-due-date': _('Due date is over.'),
456 456
                   }
457 457
        return settings.LINGO_NO_ONLINE_PAYMENT_REASONS.get(self.no_online_payment_reason,
458 458
                                reasons.get(self.no_online_payment_reason))
459 459

  
460 460
    @property
461 461
    def crypto_id(self):
462
        return aes_hex_encrypt(settings.SECRET_KEY, str(self.id))
462
        return aes_hex_encrypt(settings.SECRET_KEY, force_bytes(str(self.id)))
463 463

  
464 464

  
465 465
class Transaction(models.Model):
466 466
    regie = models.ForeignKey(Regie, on_delete=models.CASCADE, null=True)
467 467
    items = models.ManyToManyField(BasketItem, blank=True)
468 468
    remote_items = models.CharField(max_length=512)
469 469
    to_be_paid_remote_items = models.CharField(max_length=512, null=True)
470 470
    start_date = models.DateTimeField(auto_now_add=True)
combo/utils/crypto.py
11 11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 12
# GNU Affero General Public License for more details.
13 13
#
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17 17
import binascii
18 18

  
19
from Crypto.Cipher import AES
20
from Crypto.Protocol.KDF import PBKDF2
21
from Crypto import Random
19
from Cryptodome.Cipher import AES
20
from Cryptodome.Protocol.KDF import PBKDF2
21
from Cryptodome import Random
22 22

  
23 23
from django.utils import six
24 24
from django.utils.encoding import force_text
25 25

  
26 26

  
27 27
class DecryptionError(Exception):
28 28
    pass
29 29

  
setup.py
159 159
        'XStatic_OpenSans',
160 160
        'XStatic_roboto-fontface>=0.5.0.0',
161 161
        'eopayment>=1.43',
162 162
        'python-dateutil',
163 163
        'djangorestframework>=3.3, <3.8',
164 164
        'django-ratelimit<3',
165 165
        'sorl-thumbnail',
166 166
        'Pillow',
167
        'pycryptodomex',
167 168
        'pyproj',
168 169
        'pywebpush',
169 170
        'pygal',
170 171
        'lxml',
171 172
        ],
172 173
    zip_safe=False,
173 174
    cmdclass={
174 175
        'build': build,
tests/test_lingo_remote_regie.py
8 8
from requests.exceptions import ConnectionError
9 9

  
10 10
from django.apps import apps
11 11
from django.test.client import RequestFactory
12 12
from django.test import override_settings
13 13
from django.urls import reverse
14 14
from django.conf import settings
15 15
from django.core.management import call_command
16
from django.utils.encoding import force_text
16
from django.utils.encoding import force_bytes, force_text
17 17
from django.utils.six.moves.urllib import parse as urlparse
18 18
from django.utils.timezone import timedelta, now
19 19
from django.contrib.auth.models import User
20 20

  
21 21
from combo.utils import check_query, aes_hex_encrypt
22 22
from combo.data.models import Page
23 23
from combo.apps.lingo.models import (Regie, ActiveItems, ItemsHistory, SelfDeclaredInvoicePayment,
24 24
                                     Transaction, BasketItem, PaymentBackend)
......
215 215
    content = cell.render(context)
216 216
    assert content.strip() == ''
217 217

  
218 218

  
219 219
@mock.patch('combo.apps.lingo.models.Regie.pay_invoice')
220 220
@mock.patch('combo.apps.lingo.models.requests.get')
221 221
def test_anonymous_successful_item_payment(mock_get, mock_pay_invoice, app, remote_regie):
222 222
    assert remote_regie.is_remote() == True
223
    encrypt_id = aes_hex_encrypt(settings.SECRET_KEY, 'F201601')
223
    encrypt_id = aes_hex_encrypt(settings.SECRET_KEY, force_bytes('F201601'))
224 224
    # invoice with amount_paid
225 225
    invoices = copy.deepcopy(INVOICES)
226 226
    invoices[0]['amount'] = '100.00'
227 227
    invoices[0]['amount_paid'] = '23.45'
228 228
    mock_json = mock.Mock()
229 229
    mock_json.json.return_value = {'err': 0, 'data': invoices[0]}
230 230
    mock_get.return_value = mock_json
231 231
    mock_pay_invoice.return_value = mock.Mock(status_code=200)
......
289 289
    assert b_item.amount == Decimal(INVOICES[0]['amount'])
290 290
    assert b_item in trans.items.all()
291 291

  
292 292
    assert resp.status_code == 200
293 293

  
294 294
@mock.patch('combo.apps.lingo.models.requests.get')
295 295
def test_anonymous_item_payment_email_error(mock_get, app, remote_regie):
296 296
    assert remote_regie.is_remote() == True
297
    encrypt_id = aes_hex_encrypt(settings.SECRET_KEY, 'F201601')
297
    encrypt_id = aes_hex_encrypt(settings.SECRET_KEY, force_bytes('F201601'))
298 298
    mock_json = mock.Mock()
299 299
    mock_json.json.return_value = {'err': 0, 'data': INVOICES[0]}
300 300
    mock_get.return_value = mock_json
301 301
    resp = app.get('/lingo/item/%s/%s/' % (remote_regie.id, encrypt_id))
302 302
    form = resp.form
303 303
    resp = form.submit()
304 304

  
305 305
    assert resp.status_code == 302
......
358 358

  
359 359
@mock.patch('combo.apps.lingo.models.Regie.pay_invoice')
360 360
@mock.patch('combo.apps.lingo.models.requests.get')
361 361
@mock.patch('combo.apps.lingo.models.requests.post')
362 362
def test_remote_item_payment_failure(mock_post, mock_get, mock_pay_invoice, app, remote_regie):
363 363
    page = Page(title='xxx', slug='active-remote-invoices-page', template_name='standard')
364 364
    page.save()
365 365
    assert remote_regie.is_remote()
366
    encrypt_id = aes_hex_encrypt(settings.SECRET_KEY, 'F201601')
366
    encrypt_id = aes_hex_encrypt(settings.SECRET_KEY, force_bytes('F201601'))
367 367
    mock_json = mock.Mock()
368 368
    mock_json.json.return_value = {'err': 0, 'data': INVOICES[0]}
369 369
    mock_get.return_value = mock_json
370 370
    mock_pay_invoice.return_value = mock.Mock(status_code=200)
371 371
    resp = app.get('/lingo/item/%s/%s/?page=%s' % (remote_regie.id, encrypt_id, page.pk))
372 372
    form = resp.form
373 373

  
374 374
    assert 'email' in form.fields
......
427 427

  
428 428

  
429 429
@mock.patch('combo.apps.lingo.models.Regie.pay_invoice')
430 430
@mock.patch('combo.apps.lingo.models.requests.get')
431 431
def test_remote_invoice_successfull_payment_redirect(mock_get, mock_pay_invoice, app, remote_regie):
432 432
    page = Page(title='xxx', slug='active-remote-invoices-page', template_name='standard')
433 433
    page.save()
434 434
    assert remote_regie.is_remote()
435
    encrypt_id = aes_hex_encrypt(settings.SECRET_KEY, 'F201601')
435
    encrypt_id = aes_hex_encrypt(settings.SECRET_KEY, force_bytes('F201601'))
436 436
    mock_json = mock.Mock()
437 437
    mock_json.json.return_value = {'err': 0, 'data': INVOICES[0]}
438 438
    mock_get.return_value = mock_json
439 439
    mock_pay_invoice.return_value = mock.Mock(status_code=200)
440 440
    resp = app.get('/lingo/item/%s/%s/?page=%s' % (remote_regie.id, encrypt_id, page.pk))
441 441
    form = resp.form
442 442
    assert form['next_url'].value == '/active-remote-invoices-page/'
443 443
    form['email'] = 'test@example.net'
tests/test_utils.py
2 2

  
3 3
from combo.utils import (aes_hex_decrypt, aes_hex_encrypt, get_templated_url,
4 4
                         TemplateError)
5 5
from django.conf import settings
6 6
from django.test import override_settings
7 7
from django.template import Context, RequestContext
8 8
from django.test.client import RequestFactory
9 9
from django.contrib.auth.models import AnonymousUser
10
from django.utils.encoding import force_bytes
10 11

  
11 12

  
12 13
class MockUser(object):
13 14
    email = 'foo=3@example.net'
14 15
    is_authenticated = True
15 16

  
16 17
    def __init__(self, samlized=True):
17 18
        self.samlized = samlized
......
20 21
        if self.samlized:
21 22
            return 'r2&d2'
22 23
        return None
23 24

  
24 25

  
25 26
def test_crypto_url():
26 27
    invoice_id = '12-1234'
27 28
    key = settings.SECRET_KEY
28
    assert aes_hex_decrypt(key, aes_hex_encrypt(key, invoice_id)) == invoice_id
29
    assert aes_hex_decrypt(key, aes_hex_encrypt(key, force_bytes(invoice_id))) == invoice_id
29 30

  
30 31

  
31 32
def test_templated_url():
32 33
    assert get_templated_url('foobar') == 'foobar'
33 34
    assert get_templated_url('foo[]bar') == 'foo[]bar'
34 35
    assert get_templated_url('foo[bar') == 'foo[bar'
35 36
    assert get_templated_url('foo]bar') == 'foo]bar'
36 37
    assert get_templated_url('foo]bar[') == 'foo]bar['
37
-