Projet

Général

Profil

0002-templatetags-add-token-generation-with-seeds-31268.patch

Benjamin Dauvergne, 04 avril 2019 12:08

Télécharger (3,85 ko)

Voir les différences:

Subject: [PATCH 2/2] templatetags: add token generation with seeds (#31268)

- add {% token_decimal/alphanum n seed1 .. seedn %} -> generate token
  based on hash of concatenation of seed1 .. seedn
- using a form linked information (like "form_number") as seed you can
  generate deterministic unique token for a formdata, removing the need to
  store the token in a backoffice field of the formdef.
 tests/test_templates.py           | 16 ++++++++++++++++
 wcs/qommon/templatetags/qommon.py | 21 ++++++++++++++-------
 2 files changed, 30 insertions(+), 7 deletions(-)
tests/test_templates.py
460 460
    assert not any(set(token) & set('01IiOo') for token in tokens)
461 461
    t = Template('{% if token1|token_check:token2 %}ok{% endif %}')
462 462
    assert t.render({'token1': tokens[0] + ' ', 'token2': tokens[0].lower()}) == 'ok'
463

  
464

  
465
def test_token_alphanum_with_seed():
466
    tokens1 = [Template('{% token_alphanum 4 seed1 seed2 %}').render({'seed1': i, 'seed2': i + 1}) for i in range(100)]
467
    tokens2 = [Template('{% token_alphanum 4 seed1 seed2 %}').render({'seed1': i, 'seed2': i + 1}) for i in range(100)]
468
    assert all(len(token) == 4 for token in tokens1)
469
    assert all(token.upper() == token for token in tokens1)
470
    assert all(token.isalnum() for token in tokens1)
471
    assert all(len(token) == 4 for token in tokens2)
472
    assert all(token.upper() == token for token in tokens2)
473
    assert all(token.isalnum() for token in tokens2)
474
    assert tokens1 == tokens2
475
    # token_check is case insensitive
476
    t = Template('{% if token1|token_check:token2 %}ok{% endif %}')
477
    for token1, token2 in zip(tokens1, tokens2):
478
        assert t.render({'token1': token1 + ' ', 'token2': token2.lower()}) == 'ok'
wcs/qommon/templatetags/qommon.py
270 270
    return decimal(abs(parse_decimal(value)))
271 271

  
272 272

  
273
def generate_token(alphabet, length):
274
    r = random.SystemRandom()
275
    choices = [r.randrange(len(alphabet)) for i in range(length)]
273
def generate_token(alphabet, length, *seeds):
274
    if seeds:
275
        choices = []
276
        seed = ''.join(map(str, seeds))
277
        for i in range(length):
278
            seed = hashlib.sha256(seed).hexdigest()
279
            choices.append(int(seed, 16) % len(alphabet))
280
    else:
281
        r = random.SystemRandom()
282
        choices = [r.randrange(len(alphabet)) for i in range(length)]
276 283
    return ''.join([alphabet[choice] for choice in choices])
277 284

  
278 285

  
279 286
@register.simple_tag
280
def token_decimal(length=6):
287
def token_decimal(length, *seeds):
281 288
    # entropy by default is log(10^6)/log(2) = 19.93 bits
282 289
    # decimal always need more length than alphanum for the same security level
283 290
    # for 128bits security level, length must be more than log(2^128)/log(10) = 38.53 digits
284
    return generate_token(string.digits, length)
291
    return generate_token(string.digits, length, *seeds)
285 292

  
286 293

  
287 294
@register.simple_tag
288
def token_alphanum(length=4):
295
def token_alphanum(length, *seeds):
289 296
    # use of a 28 characters alphabet using uppercase letters and digits but
290 297
    # removing confusing characters and digits 0, O, 1 and I.
291 298
    # entropy by default is log(28^4)/log(2) = 19.22 bits
292 299
    # for 128 bits security level length must be more than log(2^128)/log(28) = 26.62 characters
293
    return generate_token('23456789ABCDEFGHJKLMNPQRSTUVWXYZ', length)
300
    return generate_token('23456789ABCDEFGHJKLMNPQRSTUVWXYZ', length, *seeds)
294 301

  
295 302

  
296 303
@register.filter
297
-