Newer
Older
#!/usr/bin/python
# -*- coding: UTF-8 -*-
from cStringIO import StringIO
from datetime import datetime
from email.mime.text import MIMEText
from threading import Thread
import functools
import hashlib
import json
from PIL import Image
from flask import render_template, send_from_directory
from flask import send_file
from flask import session
from flask import url_for
from flask_compress import Compress
from pyotp import random_base32
from werkzeug import abort, redirect
import gnupg
import psycopg2
import pytz
from functions import pbkdf2, AESCipher
from functions import random_data
from functions import render_jinja_html
from functions import rotate_image_upon_exif

Marco De Donno
committed
Image.MAX_IMAGE_PIXELS = 1 * 1024 * 1024 * 1024
################################################################################
debug = os.environ.get( "DEBUG", False )
baseurl = os.environ.get( "BASEURL", "" )
################################################################################

Marco De Donno
committed
# Decorators

Marco De Donno
committed
def session_field_required( field, value ):
def decorator( func ):
@functools.wraps( func )
def wrapper_login_required( *args, **kwargs ):
if not field in session:
return redirect( url_for( "login" ) )
elif not session.get( field ) == value:
return redirect( url_for( "login" ) )
return func( *args, **kwargs )
return wrapper_login_required
return decorator
def login_required( func ):
@functools.wraps( func )
def wrapper_login_required( *args, **kwargs ):
if not session.get( 'logged', False ) :
session[ 'after' ] = request.full_path
return redirect( url_for( "login", after = True ) )
return func( *args, **kwargs )
return wrapper_login_required
def referer_required( func ):
@functools.wraps( func )
def wrapper_login_required( *args, **kwargs ):
if not request.headers.get( "Referer", False ):
return "referrer needed", 404
return func( *args, **kwargs )
return wrapper_login_required
def admin_required( func ):
@functools.wraps( func )
def wrapper_login_required( *args, **kwargs ):
if not session.get( 'logged', False ) or not session.get( 'account_type', None ) == 1:
return redirect( url_for( "login" ) )
return func( *args, **kwargs )
return wrapper_login_required

Marco De Donno
committed
def redis_cache( ttl = 3600 ):
def decorator( func ):
@functools.wraps( func )
def wrapper_cache( *args, **kwargs ):
lst = []
lst.append( func.__name__ )
lst.extend( args )
index = "_".join( lst )
index = hashlib.sha256( index ).hexdigest()
d = config.redis_shared.get( index )
if d != None:
buff = StringIO()
buff.write( base64.b64decode( d ) )
buff.seek( 0 )

Marco De Donno
committed
else:
d = func( *args, **kwargs )
buff = StringIO()

Marco De Donno
committed
buff.seek( 0 )
d_cached = base64.b64encode( buff.getvalue() )
config.redis_shared.set( index, d_cached, ex = ttl )
return d
return wrapper_cache
return decorator
################################################################################
# Generic routing
def ping():
return "pong"
################################################################################
# App serving
@app.route( baseurl + '/app/<path>' )
def send_app_files( path ):
return send_from_directory( 'app', path )
@app.route( baseurl + '/static/<path>' )
def send_static_files( path ):
return send_from_directory( 'static', path )
################################################################################
# Sessions
@app.before_request
def renew_session():
session.permanent = True
app.permanent_session_lifetime = timedelta( seconds = config.session_timeout )
@app.route( baseurl + '/logout' )
def logout():
session.clear()
return redirect( url_for( 'home' ) )
@app.route( baseurl + '/login' )
def login( after = False ):
if after:
after = session.get( "after" )
session.clear()
session[ 'after' ] = after
else:
session.clear()
session[ 'need_to_check' ] = [ 'password' ]
session[ 'logged' ] = False
session[ 'session_security_key' ] = str( uuid4() )
return render_template(
"login.html",
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
session_timeout = config.session_timeout,
session_security_key = session.get( "session_security_key" )
)
@app.route( baseurl + '/do_login', methods = [ 'POST' ] )
def do_login():
need_to_check = session.get( "need_to_check", [ 'password' ] )
current_check = need_to_check[ 0 ]
except:
current_check = None
session[ 'need_to_check' ] = need_to_check
############################################################################
if current_check == "password":
q = config.db.query( 'SELECT * FROM users WHERE username = %s', ( request.form.get( "username" ), ) )
user = q.fetchone()
if user == None:
return jsonify( {
'error': False,
'logged': False
} )
form_password = request.form.get( "password", None )
if form_password == None or not pbkdf2( form_password, user[ 'password' ] ):
return jsonify( {
'error': False,
'logged': False,
} )
elif not user[ 'active' ]:
return jsonify( {
'error': False,
'logged': False,
'message': 'Your account is not activated. Please contact an administrator.'
} )
else:
session[ 'username' ] = user[ 'username' ]
session[ 'user_id' ] = user[ 'id' ]
session[ 'password_check' ] = True
session[ 'need_to_check' ].remove( current_check )
session[ 'password' ] = pbkdf2( form_password, "AES256", 50000 )
if user[ 'must_use_totp' ]:
session[ 'need_to_check' ].append( 'totp' )
if user[ 'must_use_securitykey' ]:
session[ 'need_to_check' ].append( 'securitykey' )
q = config.db.query( 'SELECT username, totp FROM users WHERE username = %s', ( session[ 'username' ], ) )
user = q.fetchone()
if not pyotp.TOTP( user[ 'totp' ] ).verify( request.form[ "totp" ], valid_window = 1 ):
return jsonify( {
'error': False,
'logged': False,
'message': 'Wrong TOTP'
session[ 'need_to_check' ].remove( current_check )
if len( session[ 'need_to_check' ] ) == 0 and session.get( "password_check", False ):
for key in [ 'process', 'need_to_check', 'password_check' ]:

Marco De Donno
committed
if key in session:
session.pop( key )
q = config.db.query( 'SELECT type FROM users WHERE username = %s', ( session[ 'username' ], ) )
user = q.fetchone()
session[ 'account_type' ] = user[ 'type' ]
if session.get( 'after', False ):
return jsonify( {
'error': False,
'logged': True,
'redirect': session.get( 'after' )
} )
else:
return jsonify( {
'error': False,
'logged': True,
} )
'next_step': session[ 'need_to_check' ][ 0 ]
################################################################################
# Reset
@app.route( baseurl + '/reset_password' )
def password_reset():
session.clear()
session[ 'process' ] = "request_password_reset"
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
return render_template(
"password_reset.html",
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss
)
@app.route( baseurl + '/do_reset_password', methods = [ 'POST' ] )
def do_password_reset():
email = request.form.get( "email", None )
Thread( target = do_password_reset_thread, args = ( email, ) ).start()
return jsonify( {
'error': False,
'message': 'OK'
} )
def do_password_reset_thread( email ):
q = config.db.query( 'SELECT id, username, email FROM users' )
users = q.fetchall()
for user in users:
if not user[ 'email' ].startswith( "pbkdf2$" ):
continue
elif pbkdf2( email, user[ 'email' ] ):
id = hashlib.sha512( random_data( 100 ) ).hexdigest()
####################################################################
data = {
'process': 'password_reset',
'process_id': id,
'user_id': user[ 'id' ]
}
data = json.dumps( data )
data = base64.b64encode( data )
config.redis_shared.set( "reset_" + id, data, ex = 24 * 3600 )
####################################################################
email_content = render_jinja_html(
"templates/email", "reset.html",
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
id = id,
url = config.domain + baseurl + "/reset_password_stage2"
)
msg = MIMEText( email_content, "html" )
msg[ 'Subject' ] = 'ICNML - User password reset'
msg[ 'From' ] = config.sender
msg[ 'To' ] = email
s = smtplib.SMTP( config.smtpserver )
s.sendmail( config.sender, [ email ], msg.as_string() )
s.quit()
break
@app.route( baseurl + '/reset_password_stage2/<id>', methods = [ 'GET', 'POST' ] )
def password_reset_stage2( id ):
id = str( id )
data = config.redis_shared.get( "reset_" + id )
if data != None:
data = base64.b64decode( data )
data = json.loads( data )
password = request.form.get( "password", None )
userid = data.get( "user_id", None )
if password != None:
password = pbkdf2( password, random_data( 50 ), 50000 )
config.db.query( "UPDATE users SET password = %s WHERE id = %s", ( password, userid ) )
config.db.commit()
config.redis_shared.delete( "reset_" + id )
return jsonify( {
'error': False,
'password_updated': True
} )
else:
return render_template(
"password_reset_stage2.html",
baseurl = baseurl,
id = id,
js = config.cdnjs,
css = config.cdncss
)
else:
return jsonify( {
'error': True,
'message': 'Reset procedure not found/expired'
} )
################################################################################
@app.route( baseurl + '/u2f/admin' )
@login_required
return render_template(
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
session_timeout = config.session_timeout,

Marco De Donno
committed
admin = int( session[ 'account_type' ] ) == 1,
keys = do_u2f_get_list_of_keys( all = True )
def do_u2f_get_list_of_keys( uid = None, all = False ):
user_id = session.get( "user_id", uid )
sql = "SELECT id, key_name as name, created_on, last_usage, usage_counter, active FROM webauthn WHERE user_id = %s"
if not all:
sql += " AND active = true"
sql += " ORDER BY usage_counter DESC"
q = config.db.query( sql, ( user_id, ) )
keys = q.fetchall()
data = []
for key in keys:
data.append( dict( key ) )
return data
@app.route( baseurl + '/u2f/begin_activate', methods = [ 'POST' ] )
@login_required
session[ 'key_name' ] = request.form.get( "key_name", None )
username = session.get( "username" )
challenge = random_base32( 64 )
ukey = random_base32( 64 )
session[ 'challenge' ] = challenge
session[ 'register_ukey' ] = ukey
make_credential_options = webauthn.WebAuthnMakeCredentialOptions(
challenge, config.rp_name, config.RP_ID,
ukey, username, username,
None
)
return jsonify( make_credential_options.registration_dict )
@app.route( baseurl + '/u2f/verify', methods = [ 'POST' ] )
@login_required
challenge = session[ 'challenge' ]
user_id = session[ 'user_id' ]
key_name = session.get( "key_name", None )
ukey = session[ 'register_ukey' ]
response = request.form
webauthn_registration_response = webauthn.WebAuthnRegistrationResponse(
config.RP_ID,
config.ORIGIN,
response,
challenge
)
try:
webauthn_credential = webauthn_registration_response.verify()
except Exception as e:
'error': True,
'message': 'Registration failed. Error: {}'.format( e )
config.db.query(
"""
INSERT INTO webauthn
( user_id, key_name, ukey, credential_id, pub_key, sign_count )
VALUES ( %s, %s, %s, %s, %s, %s )
""",
(
user_id, key_name,
ukey, webauthn_credential.credential_id,
webauthn_credential.public_key, webauthn_credential.sign_count,
)
)
config.db.commit()
return jsonify( {
'success': 'User successfully registered.'
} )
################################################################################
@app.route( baseurl + '/u2f/delete', methods = [ 'POST' ] )
@login_required
key_id = request.form.get( "key_id", False )
key_name = request.form.get( "key_name", False )
userid = session[ 'user_id' ]
try:
config.db.query( "DELETE FROM webauthn WHERE id = %s AND key_name = %s AND user_id = %s", ( key_id, key_name, userid, ) )
config.db.commit()
return jsonify( {
'error': False
} )
except Exception as e:
return jsonify( {
'error': True
} );
@app.route( baseurl + '/u2f/disable', methods = [ 'POST' ] )
@login_required
key_id = request.form.get( "key_id", False )
key_name = request.form.get( "key_name", False )
userid = session[ 'user_id' ]
try:
config.db.query( "UPDATE webauthn SET active = False WHERE id = %s AND key_name = %s AND user_id = %s", ( key_id, key_name, userid, ) )
config.db.commit()
return jsonify( {
'error': False
} )
except Exception as e:
return jsonify( {
'error': True
} );
@app.route( baseurl + '/u2f/enable', methods = [ 'POST' ] )
@login_required
key_id = request.form.get( "key_id", False )
key_name = request.form.get( "key_name", False )
userid = session[ 'user_id' ]
try:
config.db.query( "UPDATE webauthn SET active = True WHERE id = %s AND key_name = %s AND user_id = %s", ( key_id, key_name, userid, ) )
config.db.commit()
return jsonify( {
'error': False
} )
except Exception as e:
return jsonify( {
'error': True
} );
@app.route( baseurl + '/u2f/rename', methods = [ 'POST' ] )
@login_required
def u2f_rename_key():
key_id = request.form.get( "key_id", False )
key_name = request.form.get( "key_name", False )
userid = session[ 'user_id' ]
try:
config.db.query( "UPDATE webauthn SET key_name = %s WHERE id = %s AND user_id = %s", ( key_name, key_id, userid, ) )
config.db.commit()
return jsonify( {
'error': False
} )
except Exception as e:
return jsonify( {
'error': True
} );
@app.route( baseurl + '/u2f/begin_assertion' )
def u2f_begin_assertion():
user_id = session.get( "user_id" )
if 'challenge' in session:
del session[ 'challenge' ]
challenge = random_base32( 64 )
session[ 'challenge' ] = challenge
q = config.db.query( "SELECT * FROM webauthn WHERE user_id = %s AND active = true", ( user_id, ) )
key_list = q.fetchall()
credential_id_list = []
for key in key_list:
credential_id_list.append( {
'type': 'public-key',
'id': key[ 'credential_id' ],
'transports': [ 'usb', 'nfc', 'ble', 'internal' ]
} )
assertion_dict = {
'challenge': challenge,
'timeout': 60000,
'allowCredentials': credential_id_list,
'rpId': config.RP_ID,
}
return jsonify( {
'error': False,
'data': assertion_dict
@app.route( baseurl + '/u2f/verify_assertion', methods = [ 'POST' ] )
def verify_assertion():
challenge = session.get( 'challenge' )
assertion_response = request.form
credential_id = assertion_response.get( 'id' )
q = config.db.query( "SELECT * FROM webauthn WHERE credential_id = %s", ( credential_id, ) )
user = q.fetchone()
webauthn_user = webauthn.WebAuthnUser(
None, session[ 'username' ], None, None,
user[ 'credential_id' ], user[ 'pub_key' ], user[ 'sign_count' ], config.RP_ID
)
webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
webauthn_user,
assertion_response,
challenge,
config.ORIGIN,
uv_required = False
)
try:
sign_count = webauthn_assertion_response.verify()
except Exception as e:
return jsonify( {
'error': True,
'message': 'Assertion failed. Error: {}'.format( e )
} )
dt = datetime.now( pytz.timezone( 'Europe/Zurich' ) )
q = config.db.query( "UPDATE webauthn SET sign_count = %s, last_usage = %s, usage_counter = usage_counter + 1 WHERE credential_id = %s", ( sign_count, dt, credential_id, ) )
session[ 'need_to_check' ].remove( "securitykey" )
do_login()
return jsonify( {
'error': False
} )
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
################################################################################
# New user
@app.route( baseurl + '/signin' )
def new_user():
q = config.db.query( "SELECT id, name FROM account_type WHERE can_singin = true" )
r = q.fetchall()
account_type = []
for rr in r:
account_type.append( dict( rr ) )
return render_template(
"signin.html",
baseurl = baseurl,
list_account_type = account_type,
js = config.cdnjs,
css = config.cdncss
)
@app.route( baseurl + '/do_signin', methods = [ 'POST' ] )
def add_account_request_to_db():
try:
first_name = request.form[ 'first_name' ]
last_name = request.form[ 'last_name' ]
email = request.form[ 'email' ]
account_type = request.form[ 'account_type' ]
uuid = str( uuid4() )
config.db.query(
"""
INSERT INTO signin_requests
( first_name, last_name, email, account_type, uuid )
VALUES ( %s, %s, %s, %s, %s )
""",
( first_name, last_name, email, account_type, uuid )
)
config.db.commit()
return jsonify( {
'error': False,
'uuid': uuid
} )
except:
return jsonify( {
'error': True
} )
@app.route( baseurl + '/validate_signin' )
@admin_required
def validate_signin():
q = config.db.query( """
SELECT signin_requests.*, account_type.name as account_type
FROM signin_requests
LEFT JOIN account_type ON signin_requests.account_type = account_type.id
WHERE signin_requests.status = 'pending'
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
""" )
r = q.fetchall()
users = []
for rr in r:
users.append( dict( rr ) )
return render_template(
"validate_signin.html",
baseurl = baseurl,
users = users,
js = config.cdnjs,
css = config.cdncss
)
@app.route( baseurl + '/do_validate_signin', methods = [ 'POST' ] )
@admin_required
def do_validate_signin():
request_id = request.form.get( "id" )
q = config.db.query( 'SELECT * FROM signin_requests WHERE id = %s', ( request_id, ) )
s = q.fetchone()
s = dict( s )
r = {}
r[ 'user' ] = s
r[ 'user' ][ 'request_time' ] = str( r[ 'user' ][ 'request_time' ] )
r[ 'user' ][ 'validation_time' ] = str( r[ 'user' ][ 'validation_time' ] )
r[ 'acceptance' ] = {}
r[ 'acceptance' ][ 'username' ] = session[ 'username' ]
r[ 'acceptance' ][ 'time' ] = str( datetime.now() )
j = json.dumps( r )
challenge = base64.b64encode( j )
challenge = challenge.replace( "=", "" )
session[ 'validation_user_challenge' ] = challenge
############################################################################
user_id = session[ 'user_id' ]
q = config.db.query( "SELECT * FROM webauthn WHERE user_id = %s ORDER BY last_usage DESC LIMIT 1", ( user_id, ) )
key = q.fetchone()
webauthn_user = webauthn.WebAuthnUser(
key[ 'ukey' ], session[ 'username' ], session[ 'username' ], None,
key[ 'credential_id' ], key[ 'pub_key' ], key[ 'sign_count' ], config.RP_ID
)
webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( webauthn_user, challenge )
return jsonify( {
'error': False,
'data': webauthn_assertion_options.assertion_dict
} )
@app.route( baseurl + '/do_validate_signin_2', methods = [ 'POST' ] )
@admin_required
def do_validate_signin_2():
challenge = session.get( 'validation_user_challenge' )
assertion_response = request.form
assertion_response_s = base64.b64encode( json.dumps( assertion_response ) )
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
credential_id = assertion_response.get( 'id' )
q = config.db.query( "SELECT * FROM webauthn WHERE credential_id = %s", ( credential_id, ) )
user = q.fetchone()
webauthn_user = webauthn.WebAuthnUser(
user[ 'ukey' ], session[ 'username' ], session[ 'username' ], None,
user[ 'credential_id' ], user[ 'pub_key' ], user[ 'sign_count' ], "icnml.unil.ch"
)
webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
webauthn_user,
assertion_response,
challenge,
config.ORIGIN,
uv_required = False
)
try:
sign_count = webauthn_assertion_response.verify()
except Exception as e:
return jsonify( {
'error': True,
'message': 'Assertion failed. Error: {}'.format( e )
} )
############################################################################
if len( challenge ) % 4 != 0:
challenge += "=" * ( 4 - ( len( challenge ) % 4 ) )
newuser = base64.b64decode( challenge )
newuser = json.loads( newuser )
user_type = newuser[ 'user' ][ 'account_type' ]
email = newuser[ 'user' ][ 'email' ]
email_hash = pbkdf2( email, random_data( 100 ), 50000 )
request_uuid = newuser[ 'user' ][ 'uuid' ]

Marco De Donno
committed
request_id = newuser[ 'user' ][ 'id' ]
username_id = newuser[ 'user' ][ 'username_id' ]
n = config.db.query( "SELECT name FROM account_type WHERE id = %s", ( user_type, ) )
n = n.fetchone()
n = n[ 0 ]

Marco De Donno
committed
username = n + "_" + str( username_id )
username = username.lower()
try:
config.db.query( "UPDATE signin_requests SET validation_time = now(), assertion_response = %s, status = 'validated'", ( assertion_response_s, ) )
config.db.query( "INSERT INTO users ( username, email, type ) VALUES ( %s, %s, %s )", ( username, email_hash, user_type ) )
config.db.commit()

Marco De Donno
committed
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
except:
return jsonify( {
'error': True,
'message': 'Can not insert into database.'
} )
############################################################################
email_content = render_jinja_html(
"templates/email", "signin.html",
username = username,
url = "https://icnml.unil.ch/config/" + newuser[ 'user' ][ 'uuid' ]
)
msg = MIMEText( email_content, "html" )
msg[ 'Subject' ] = 'ICNML - Login information'
msg[ 'From' ] = config.sender
msg[ 'To' ] = email
s = smtplib.SMTP( config.smtpserver )
s.sendmail( config.sender, [ email ], msg.as_string() )
s.quit()
############################################################################
return jsonify( {
'error': False
} )
@app.route( baseurl + '/config/<uuid>' )
def config_new_user( uuid ):
session.clear()
q = config.db.query( "SELECT email FROM signin_requests WHERE uuid = %s", ( uuid, ) )
r = q.fetchone()
try:
email = r[ 'email' ]
session[ 'signin_user_validation_email' ] = email
session[ 'signin_user_validation_uuid' ] = uuid
return render_template(
"user_config.html",
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
session_timeout = config.session_timeout
)
except:
return redirect( url_for( 'home' ) )
@app.route( baseurl + '/do_config', methods = [ 'POST' ] )
def do_config_new_user():
email = session[ 'signin_user_validation_email' ]
uuid = session[ 'signin_user_validation_uuid' ]
username = request.form.get( "username" )
password = request.form.get( "password" )
session[ 'username' ] = username
############################################################################
q = config.db.query( "SELECT count(*) FROM signin_requests WHERE uuid = %s AND email = %s", ( uuid, email, ) )
r = q.fetchone()
r = r[ 0 ]
if r == 0:
return jsonify( {
'error': True,
'message': "no signin request"
} )
q = config.db.query( "SELECT * FROM users WHERE username = %s", ( username, ) )
user = q.fetchone()
if user == None:
return jsonify( {
'error': True,
'message': "no user"
} )
elif not password.startswith( "pbkdf2" ):
return jsonify( {
'error': True,
'message': "password not in the correct format"
} )
elif not pbkdf2( email, user[ 'email' ] ):
return jsonify( {
'error': True,
'message': "email not corresponding to the request form"
} )
elif user.get( 'password', None ) != None:
return jsonify( {
'error': True,
'message': "password already set"
} )
############################################################################
password = pbkdf2( password, random_data( 65 ), 50000 )
q = config.db.query( "UPDATE users SET password = %s WHERE username = %s", ( password, username, ) )
config.db.commit()
session[ 'username' ] = username
############################################################################
return jsonify( {
'error': False
} )
@app.route( baseurl + '/totp_help' )
def totp_help():
return render_template(
"totp_help.html",
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
)
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
################################################################################
# QR Code generation
def renew_secret():
secret = random_base32( 40 )
session[ 'secret' ] = secret
return secret
def get_secret():
secret = session.get( "secret", None )
if secret == None:
secret = renew_secret()
return secret
@app.route( baseurl + '/set_secret' )
def set_secret():
config.db.query( "UPDATE users SET totp = %s WHERE username = %s", ( session[ 'secret' ], session[ 'username' ], ) )
config.db.commit()
return jsonify( {
'error': False
} )
@app.route( baseurl + '/secret' )
def request_secret():
get_secret()
return jsonify( {
'error': False,
'secret': session[ 'secret' ]
} )
@app.route( baseurl + '/new_secret' )
@login_required
def request_renew_secret():
renew_secret()
return jsonify( {
'error': False,
'secret': session[ 'secret' ]
} )
@app.route( baseurl + '/qrcode' )
def send_qrcode():
if 'username' in session:
qrcode_value = 'otpauth://totp/ICNML%20' + session[ 'username' ] + '?secret=' + get_secret()
else:
qrcode_value = 'otpauth://totp/ICNML?secret=' + get_secret()
img = qrcode.make( qrcode_value )
temp = StringIO()
img.save( temp, format = "png" )
temp.seek( 0 )
return send_file( temp, mimetype = 'image/png' )
@app.route( baseurl + '/user_qrcode' )
@login_required
return render_template(
"qrcode.html",
baseurl = baseurl,
secret = get_secret(),
js = config.cdnjs,
css = config.cdncss
)
################################################################################
# Data decryption
def do_decrypt( data ):
try:
return AESCipher( session[ 'password' ] ).decrypt( data )
except:
return None
def do_encrypt( data ):
return AESCipher( session[ 'password' ] ).encrypt( data )
################################################################################
# File upload
@app.route( baseurl + '/upload', methods = [ 'GET', 'POST' ] )
@login_required
def upload_file():
upload_type = request.form.get( "upload_type", None )
if upload_type == None:
return jsonify( {
'error': True,
'msg': 'Must specify a file type to upload a file'
} )
if request.method == 'POST':
if 'file' not in request.files:
return jsonify( { 'error': True, 'msg': 'No file in the POST request' } )
elif 'upload_id' not in request.form:
return jsonify( { 'error': True, 'msg': 'No upload_id' } )
else:
try:
upload_id = request.form.get( "upload_id" )
sql = "SELECT id FROM submissions WHERE uuid = %s"
r = config.db.query( sql, ( upload_id, ) )
upload_id = r.fetchone()[ 'id' ]
except:
return jsonify( {
'error': True
} )
file = request.files[ 'file' ]
filename = do_encrypt( file.filename )
file_uuid = str( uuid4() )
fp = StringIO()
file.save( fp )
file_size = fp.tell()
fp.seek( 0 )
if upload_type in [ 'latent_target', 'latent_incidental', 'tenprint_card_front', 'tenprint_card_back' ]:
img = Image.open( fp )
img_format = img.format
width, height = img.size
res = int( img.info[ 'dpi' ][ 0 ] )
img = rotate_image_upon_exif( img )
buff = StringIO()
img.save( buff, format = img_format )
buff.seek( 0 )
file_data = buff.getvalue()
if upload_type in [ 'tenprint_card_front', 'tenprint_card_back' ]:
create_thumbnail( file_uuid, img )
else:
file_data = fp.getvalue()
file_data = base64.b64encode( file_data )
sql = "SELECT id FROM files_type WHERE name = %s"
upload_type_id = config.db.query( sql, ( upload_type, ) ).fetchone()[ 0 ]
####################################################################
if upload_type == "consent_form":
file_data = gpg.encrypt( file_data, config.gpg_key )
file_data = str( file_data )
file_data = base64.b64encode( file_data )
sql = "INSERT INTO cf ( uuid, data ) VALUES ( %s, %s )"
config.db.query( sql , ( file_uuid, file_data, ) )
config.db.commit()
else:
sql = "INSERT INTO files ( folder, creator, filename, type, format, size, width, height, resolution, uuid, data ) VALUES ( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s )"
data = ( upload_id, session[ 'user_id' ], filename, upload_type_id, img_format, file_size, width, height, res, file_uuid, file_data )
config.db.query( sql, data )
config.db.commit()
return jsonify( {
'error': False,
'filename': filename,
'filesize': file_size,
'uuid': file_uuid
} )
else:
return abort( 403 )
################################################################################
return render_template(
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
session_timeout = config.session_timeout,
session_security_key = session.get( "session_security_key" )
@app.route( baseurl + '/submission/do_new', methods = [ "GET", "POST" ] )
email = request.form.get( "email", False )
if email:
# Check for duplicate base upon the email data
sql = "SELECT id, email_hash FROM submissions WHERE submitter_id = %s"
r = config.db.query( sql, ( session[ 'user_id' ], ) )
for case in r.fetchall():
if pbkdf2( email, case[ 'email_hash' ] ):
return jsonify( {
'error': True,
'msg': "Email already used"
} )
break
email_aes = do_encrypt( email )
email_hash = pbkdf2( email, random_data( 50 ), 50000 )
upload_nickname = request.form.get( "upload_nickname", None )
upload_nickname = do_encrypt( upload_nickname )
submitter_id = session[ 'user_id' ]
status = "pending"
sql = "INSERT INTO submissions ( uuid, email_aes, email_hash, nickname, status, submitter_id ) VALUES ( %s, %s, %s, %s, %s, %s ) RETURNING id"
data = ( id, email_aes, email_hash, upload_nickname, status, submitter_id )
config.db.commit()
return jsonify( {
'error': False,
'id': id
} )
else:
return jsonify( {
'error': True,
'msg': "Email not provided"
} )
@app.route( baseurl + '/submission/<id>/add_files' )
@login_required
def submission_upload_tplp( id ):
try:
sql = "SELECT email_aes as email, nickname, created_time FROM submissions WHERE submitter_id = %s AND uuid = %s"
r = config.db.query( sql, ( session[ 'user_id' ], id ) )
user = r.fetchone()
for key in [ 'email', 'nickname' ]:
user[ key ] = do_decrypt( user[ key ] )
return render_template(
"submission/add_files.html",
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
session_timeout = config.session_timeout,
upload_id = id,
session_security_key = session.get( "session_security_key" ),
except:
return jsonify( {
'error': True,
'msg': "Case not found"
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
} )
@app.route( baseurl + '/submission/<id>/consent_form' )
@login_required
def submission_consent_form( id ):
try:
sql = "SELECT email_aes as email, nickname, created_time FROM submissions WHERE submitter_id = %s AND uuid = %s"
r = config.db.query( sql, ( session[ 'user_id' ], id ) )
user = r.fetchone()
for key in [ 'email', 'nickname' ]:
user[ key ] = do_decrypt( user[ key ] )
return render_template(
"submission/consent_form.html",
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
session_timeout = config.session_timeout,
upload_id = id,
session_security_key = session.get( "session_security_key" ),
**user
)
except:
return jsonify( {
'error': True,
'msg': "Case not found"
@app.route( baseurl + '/submission/<id>/set/nickname', methods = [ 'POST' ] )

Marco De Donno
committed
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
@login_required
def submission_update_nickname( id ):
nickname = request.form.get( "nickname", None )
if nickname != None and len( nickname ) != 0:
try:
nickname = do_encrypt( nickname )
sql = "UPDATE submissions SET nickname = %s WHERE uuid = %s"
config.db.query( sql, ( nickname, id, ) )
config.db.commit()
return jsonify( {
'error': False
} )
except:
return jsonify( {
'error': True,
'message': "DB error"
} )
else:
return jsonify( {
'error': True,
'message': "No new nickname in the POST request"
} )
sql = "SELECT * FROM submissions WHERE submitter_id = %s ORDER BY created_time DESC"
r = config.db.query( sql, ( session[ 'user_id' ], ) )
q = r.fetchall()
donors = []
for donor in q:
donors.append( {
'id': donor.get( "id", None ),
'email': do_decrypt( donor.get( "email_aes", None ) ),
'nickname': do_decrypt( donor.get( "nickname", None ) ),
'uuid': donor.get( "uuid", None )
} )
return render_template(
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
session_timeout = config.session_timeout,
donors = donors,
session_security_key = session.get( "session_security_key" )
@app.route( baseurl + '/submission/<id>/latent/list' )
@app.route( baseurl + '/submission/<id>/latent/list/<ltype>' )
@login_required
def submission_latent_list( id, ltype = 'all' ):
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
if ltype in [ 'target', 'incidental', 'all' ]:
sql = "SELECT id, nickname FROM submissions WHERE uuid = %s AND submitter_id = %s"
r = config.db.query( sql, ( id, session[ 'user_id' ], ) )
case_id, nickname = r.fetchone()
nickname = do_decrypt( nickname )
sql = """
SELECT files.uuid, files.filename, files.size, files.creation_time
FROM files
LEFT JOIN files_type ON files.type = files_type.id
WHERE folder = %s AND
"""
if ltype == "target":
sql += " files_type.name = 'latent_target'"
elif ltype == "incidental":
sql += " files_type.name = 'latent_incidental'"
elif ltype == "all":
sql += " ( files_type.name = 'latent_target' OR files_type.name = 'latent_incidental' )"
sql += " ORDER BY files.id DESC"
r = config.db.query( sql, ( case_id, ) )
files = r.fetchall()
for i, v in enumerate( files ):
v[ 'filename' ] = do_decrypt( v[ 'filename' ] )
v[ 'size' ] = round( ( float( v[ 'size' ] ) / ( 1024 * 1024 ) ) * 100 ) / 100
return render_template(
"submission/latent_list.html",
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
session_timeout = config.session_timeout,
submission_id = id,
latent_type = ltype,
files = files,
nickname = nickname,
session_security_key = session.get( "session_security_key" )
)
else:
return abort( 403 )
@app.route( baseurl + '/submission/<id>/latent/<lid>' )
@login_required
def submission_latent( id, lid ):
sql = "SELECT id, nickname FROM submissions WHERE uuid = %s"
r = config.db.query( sql, ( id, ) )
submission_id, nickname = r.fetchone()
nickname = do_decrypt( nickname )
sql = """
SELECT
files.uuid, files.filename, files.note,
files.format, files.resolution, files.width, files.height, files.size,
files.creation_time, files.type,
files_type.name as file_type
FROM files
LEFT JOIN files_type ON files.type = files_type.id
WHERE
folder = %s AND
files.uuid = %s
"""
r = config.db.query( sql, ( submission_id, lid, ) )

Marco De Donno
committed
file = r.fetchone()
file[ 'size' ] = round( 100 * float( file[ 'size' ] ) / ( 1024 * 1024 ) ) / 100
file[ 'filename' ] = do_decrypt( file[ 'filename' ] )
file[ 'note' ] = do_decrypt( file[ 'note' ] )
file[ 'file_type' ] = file[ 'file_type' ].replace( "latent_", "" )
return render_template(
"submission/latent.html",
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
session_timeout = config.session_timeout,
submission_id = id,

Marco De Donno
committed
file = file,
session_security_key = session.get( "session_security_key" )
)
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
@app.route( baseurl + '/submission/<id>/latent/<lid>/pfsp' )
@login_required
def submission_latent_pfsp( id, lid ):
sql = "SELECT id, nickname FROM submissions WHERE uuid = %s"
r = config.db.query( sql, ( id, ) )
submission_id, nickname = r.fetchone()
nickname = do_decrypt( nickname )
sql = """
SELECT
files.uuid, files.filename, files.note,
files.format, files.resolution, files.width, files.height, files.size,
files.creation_time, files.type,
files_type.name as file_type
FROM files
LEFT JOIN files_type ON files.type = files_type.id
WHERE
folder = %s AND
files.uuid = %s
"""
r = config.db.query( sql, ( submission_id, lid, ) )
file = r.fetchone()
file[ 'size' ] = round( 100 * float( file[ 'size' ] ) / ( 1024 * 1024 ) ) / 100
file[ 'filename' ] = do_decrypt( file[ 'filename' ] )
file[ 'note' ] = do_decrypt( file[ 'note' ] )
file[ 'file_type' ] = file[ 'file_type' ].replace( "latent_", "" )
sql = "SELECT pfsp FROM latent_info WHERE uuid = %s"
try:
current_pfsp = config.db.query( sql, ( lid, ) ).fetchone()[ 0 ]
except:
current_pfsp = None
for z in pfsp.zones:
if z[ 'desc' ] == current_pfsp:
current_pfsp = ",".join( z[ 'sel' ] )
return render_template(
"submission/latent_pfsp.html",
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
session_timeout = config.session_timeout,
submission_id = id,
nickname = nickname,
file = file,
pfsp_zones = pfsp.zones,
current_pfsp = current_pfsp,
session_security_key = session.get( "session_security_key" )
)
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
@app.route( baseurl + '/submission/<id>/latent/<lid>/set/pfsp', methods = [ 'POST' ] )
@login_required
def submission_latent_pfsp_set( id, lid ):
pfsp = request.form.get( "pfsp" )
sql = "SELECT id FROM latent_info WHERE uuid = %s"
q = config.db.query( sql, ( lid, ) ).fetchone()
if q == None:
sql = "INSERT INTO latent_info ( uuid, pfsp ) values ( %s, %s )"
config.db.query( sql, ( lid, pfsp, ) )
else:
sql = "UPDATE latent_info SET pfsp = %s WHERE uuid = %s"
config.db.query( sql, ( pfsp, lid, ) )
config.db.commit()
return jsonify( {
'error': False
} )
@app.route( baseurl + '/submission/<id>/latent/<lid>/delete' )
@login_required
def submission_latent_delete( id, lid ):
sql = "SELECT id FROM submissions WHERE submitter_id = %s AND uuid = %s"
q = config.db.query( sql, ( session[ 'user_id' ], id, ) )
if q != None:
sql = "DELETE FROM files WHERE creator = %s AND uuid = %s"
config.db.query( sql, ( session[ 'user_id' ], lid, ) )
config.db.commit()
return jsonify( {
'error': False
} )
else:
return jsonify( {
'error': True
} )
################################################################################
# Image processing
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
@app.route( baseurl + '/image/file/<id>/preview' )
@login_required
def image_file_serve( id ):
img, _ = image_serve( "thumbnails", id )
if img == None:
img, _ = image_serve( "files", id )
if img == None:
return abort( 404 )
img = create_thumbnail( id, img )
buff = pil2buffer( img, "PNG" )
return send_file( buff, mimetype = "image/png" )
@app.route( baseurl + '/image/segment/<tid>/<pc>' )
@login_required
def image_segment_serve( tid, pc ):
img, id = image_serve( "files_segments", ( tid, pc ) )
img = create_thumbnail( id, img )
buff = pil2buffer( img, "PNG" )
return send_file( buff, mimetype = "image/png" )
@app.route( baseurl + '/image/template/<tid>/<t>' )
@app.route( baseurl + '/image/template/<tid>/<t>/<action>' )
def image_tp_template( tid, t, action = "full" ):
if t in [ 'front', 'back' ]:
img, _ = image_serve( "tenprint_cards", ( tid, t ) )
if action == "preview":
img.thumbnail( ( 500, 500 ) )
buff = pil2buffer( img, "PNG" )
return send_file( buff, mimetype = "image/png" )
else:
return abort( 403 )
def image_serve( db, id ):
if db == "files_segments" and isinstance( id, tuple ):
tp, pc = id
sql = "SELECT data, uuid FROM " + db + " WHERE tenprint = %s AND pc = %s"
p = ( tp, pc, )
elif db in [ "files", "thumbnails" ]:
sql = "SELECT data, uuid FROM " + db + " WHERE uuid = %s"
p = ( id, )
elif db == "tenprint_cards":
id, t = id
sql = "SELECT image_" + t + ", id FROM " + db + " WHERE id = %s"
p = ( id, )
else:
raise Exception( "table not authorized" )
data = config.db.query( sql, p ).fetchone()
if data == None:
return None, None
img, rid = data
img = str2img( img )
def str2img( data ):
if data == None:
return None
else:
img = base64.b64decode( data )
buff = StringIO()
buff.write( img )
buff.seek( 0 )
img = Image.open( buff )
return img
@login_required
def img_info( id ):
d = do_img_info( id )
if d != None:
return jsonify( d )
else:
return abort( 404 )
def do_img_info( id ):
sql = "SELECT size, width, height, resolution, format FROM files WHERE uuid = %s"
r = config.db.query( sql, ( id, ) )
d = r.fetchone()
if d != None:
return dict( d )
else:
return None
def create_thumbnail( file_uuid, img ):
img.thumbnail( ( 1000, 1000 ) )
width, height = img.size
file_format = img.format
buff = StringIO()
img.save( buff, format = img.format )
img_size = buff.tell()
buff.seek( 0 )
img_data = buff.getvalue()
img_data = base64.b64encode( img_data )
sql = "INSERT INTO thumbnails ( uuid, width, height, size, format, data ) VALUES ( %s, %s, %s, %s, %s, %s )"
data = ( file_uuid, width, height, img_size, img.format, img_data, )
config.db.query( sql, data )
config.db.commit()
@app.route( baseurl + '/image/segment/<id>/start' )

Marco De Donno
committed
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
@login_required
def image_tenprint_segmentation( id ):
ret = do_image_tenprint_segmentation( id )
return jsonify( {
'error': False,
'data': ret
} )
def do_image_tenprint_segmentation( id ):
sql = "SELECT size, resolution, type, format, data FROM files WHERE uuid = %s"
r = config.db.query( sql, ( id, ) )
img = r.fetchone()
res = img[ 'resolution' ]
img_format = img[ 'format' ]
t = {
1: 'front',
2: 'back'
}[ img[ 'type' ] ]
img = base64.b64decode( img[ 'data' ] )
buff = StringIO()
buff.write( img )
buff.seek( 0 )
img = Image.open( buff )
sql = "SELECT template FROM file_template WHERE file = %s"
r = config.db.query( sql, ( id, ) )
template_id = r.fetchone()[ 'template' ]
zones = get_tenprint_template_zones( template_id, t )
for z in zones:
tl_x, tl_y, br_x, br_y = map( lambda v: v * res / 2.54 , [ z[ "tl_x" ], z[ "tl_y" ], z[ "br_x" ], z[ "br_y" ] ] )
tmp = img.crop( ( tl_x, tl_y, br_x, br_y ) )
buff = StringIO()
tmp.save( buff, format = img_format )
buff.seek( 0 )
file_data = buff.getvalue()
file_data = base64.b64encode( file_data )
sql = "SELECT id FROM files_segments WHERE tenprint = %s AND pc = %s"

Marco De Donno
committed
q = config.db.query( sql, ( id, z[ 'pc' ], ) ).fetchone()
if q == None:
sql = "INSERT INTO files_segments ( tenprint, uuid, pc, data ) VALUES ( %s, %s, %s, %s )"
data = ( id, str( uuid4() ), z[ 'pc' ], file_data )
config.db.query( sql, data )
else:
sql = "UPDATE files_segments SET data = %s WHERE tenprint = %s AND pc = %s"

Marco De Donno
committed
config.db.query( sql, data )
config.db.commit()
return True

Marco De Donno
committed
################################################################################

Marco De Donno
committed
@app.route( baseurl + '/submission/<id>/tenprint/list' )

Marco De Donno
committed
@login_required
sql = "SELECT id, nickname FROM submissions WHERE uuid = %s"

Marco De Donno
committed
r = config.db.query( sql, ( id, ) )
submission_id, nickname = r.fetchone()
nickname = do_decrypt( nickname )

Marco De Donno
committed
sql = """
SELECT
id, filename, uuid, type, creation_time
FROM files
WHERE folder = %s AND ( type = 1 OR type = 2 )
ORDER BY creation_time DESC
"""

Marco De Donno
committed
r = config.db.query( sql, ( submission_id, ) )
q = r.fetchall()
tenprint_cards = {
'1': [],
'2': []
}

Marco De Donno
committed
for tenprint in q:
tenprint_cards[ str( tenprint[ 'type' ] ) ].append( {

Marco De Donno
committed
'id': tenprint.get( "id", None ),
'filename': do_decrypt( tenprint.get( "filename", None ) ),
'uuid': tenprint.get( "uuid", None ),
'type': tenprint.get( "type", None )

Marco De Donno
committed
} )
return render_template(

Marco De Donno
committed
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
session_timeout = config.session_timeout,
tenprint_cards_front = tenprint_cards[ '1' ],
tenprint_cards_back = tenprint_cards[ '2' ],
nickname = nickname,
session_security_key = session.get( "session_security_key" )

Marco De Donno
committed
)
@app.route( baseurl + '/submission/<id>/tenprint/<tid>' )

Marco De Donno
committed
@login_required
sql = "SELECT id, nickname FROM submissions WHERE uuid = %s"

Marco De Donno
committed
r = config.db.query( sql, ( id, ) )
submission_id, nickname = r.fetchone()
nickname = do_decrypt( nickname )

Marco De Donno
committed
sql = """
SELECT
files.uuid, files.filename, files.note,
files.format, files.resolution, files.width, files.height, files.size,
files.creation_time, files.type,

Marco De Donno
committed
file_template.template
FROM files

Marco De Donno
committed
LEFT JOIN file_template ON files.uuid = file_template.file

Marco De Donno
committed
WHERE
folder = %s AND
files.uuid = %s
"""
r = config.db.query( sql, ( submission_id, tid, ) )

Marco De Donno
committed
file = r.fetchone()
file[ 'size' ] = round( 100 * float( file[ 'size' ] ) / ( 1024 * 1024 ) ) / 100
file[ 'filename' ] = do_decrypt( file[ 'filename' ] )
file[ 'note' ] = do_decrypt( file[ 'note' ] )

Marco De Donno
committed
if file[ 'type' ] == 1:
t = 'front'
elif file[ 'type' ] == 2:
t = 'back'
############################################################################

Marco De Donno
committed
try:
sql = 'SELECT width, height, image_resolution FROM tenprint_cards WHERE id = %s LIMIT 1'
r = config.db.query( sql, ( file[ 'template' ], ) )
tmp = r.fetchone()
card_info = {
'width': int( round( float( tmp[ 'width' ] ) / 2.54 * tmp[ 'image_resolution' ] ) ),
'height': int( round( float( tmp[ 'height' ] ) / 2.54 * tmp[ 'image_resolution' ] ) ),
'width_cm': tmp[ 'width' ],
'height_cm': tmp[ 'height' ]
}
except:
card_info = {
'width': 0,
'height': 0,
'width_cm': 0,
'height_cm': 0
}

Marco De Donno
committed

Marco De Donno
committed
############################################################################
sql = "SELECT id, country_code, name, width, height, size_display FROM tenprint_cards ORDER BY country_code"
tenprint_templates = config.db.query( sql ).fetchall()

Marco De Donno
committed
############################################################################
zones = get_tenprint_template_zones( file[ 'template' ], t )

Marco De Donno
committed
datacolumns = [ 'tl_x', 'tl_y', 'br_x', 'br_y', 'angle' ]
############################################################################
sql = 'SELECT width, height, resolution FROM files WHERE uuid = %s LIMIT 1'
r = config.db.query( sql, ( tid, ) )
img_info = r.fetchone()
svg_hw_factor = float( img_info[ 'width' ] ) / float( img_info[ 'height' ] )
return render_template(

Marco De Donno
committed
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
session_timeout = config.session_timeout,
upload_id = id,
tenprint_id = tid,
file = file,
session_security_key = session.get( "session_security_key" ),

Marco De Donno
committed
t = t,
card_id = file[ 'uuid' ],
card_info = card_info,
img_info = img_info,
svg_hw_factor = svg_hw_factor,
zones = zones,

Marco De Donno
committed
datacolumns = datacolumns,
tenprint_templates = tenprint_templates

Marco De Donno
committed
)
@app.route( baseurl + '/submission/<id>/tenprint/<tid>/delete' )
@login_required
def submission_tenprint_delete( id, tid ):
sql = "SELECT id FROM submissions WHERE submitter_id = %s AND uuid = %s"
q = config.db.query( sql, ( session[ 'user_id' ], id, ) )
if q != None:
sql = "DELETE FROM files WHERE creator = %s AND uuid = %s"
config.db.query( sql, ( session[ 'user_id' ], tid, ) )
config.db.commit()
return jsonify( {
'error': False
} )
else:
return jsonify( {
'error': True
} )
@app.route( baseurl + '/submission/<id>/tenprint/<file>/set/template', methods = [ 'GET', 'POST' ] )

Marco De Donno
committed
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
@login_required
def submission_tenprint_set_template( id, file ):
template = request.form.get( "template" )
sql = "SELECT id FROM file_template WHERE file = %s"
q = config.db.query( sql, ( file, ) ).fetchone()
if q == None:
sql = "INSERT INTO file_template ( file, template ) VALUES ( %s, %s )"
config.db.query( sql, ( file, template, ) )
config.db.commit()
else:
sql = "UPDATE file_template SET template = %s WHERE file = %s"
config.db.query( sql, ( template, file, ) )
config.db.commit()
return jsonify( {
'error': False
} )
@app.route( baseurl + '/submission/<id>/<t>/<file>/set/note', methods = [ 'POST' ] )
@login_required
def submission_file_set_note( id, t, file ):
note = request.form.get( "note" )
note = do_encrypt( note )
sql = "UPDATE files SET note = %s WHERE uuid = %s RETURNING id"
config.db.query( sql, ( note, file, ) )
config.db.commit()
return jsonify( {
'error': False
} )

Marco De Donno
committed
################################################################################
# Tenprint segments
@app.route( baseurl + '/submission/<id>/tenprint/<tid>/segment/list' )

Marco De Donno
committed
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
@login_required
def submission_tenprint_segments_list( id, tid ):
sql = "SELECT id, nickname FROM submissions WHERE uuid = %s"
r = config.db.query( sql, ( id, ) )
submission_id, nickname = r.fetchone()
nickname = do_decrypt( nickname )
sql = "SELECT uuid, filename FROM files WHERE folder = %s AND files.uuid = %s"
r = config.db.query( sql, ( submission_id, tid, ) )
file = r.fetchone()
filename = do_decrypt( file[ 'filename' ] )
tid = file[ 'uuid' ]
############################################################################
sql = """
SELECT files_segments.pc, files_segments.data, pc.name
FROM files_segments
LEFT JOIN pc ON pc.id = files_segments.pc
WHERE tenprint = %s
"""
segments = config.db.query( sql, ( tid, ) ).fetchall()
############################################################################
return render_template(
"submission/segment_list.html",
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
session_timeout = config.session_timeout,
upload_id = id,
tenprint_id = tid,
nickname = nickname,
filename = filename,
submission_id = id,
tid = tid,
segments = segments,
session_security_key = session.get( "session_security_key" ),
)
@app.route( baseurl + '/submission/<id>/tenprint/<tid>/segment/<pc>' )
@login_required
def submission_segment( id, tid, pc ):
pc = int( pc )
pc_list = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 25, 27 ]
if not pc in pc_list:
return redirect( url_for( "submission_tenprint_segments_list", id = id, tid = tid ) )
else:
sql = "SELECT id, nickname FROM submissions WHERE uuid = %s"
r = config.db.query( sql, ( id, ) )
submission_id, nickname = r.fetchone()
nickname = do_decrypt( nickname )
sql = "SELECT uuid, filename, type FROM files WHERE folder = %s AND files.uuid = %s"
r = config.db.query( sql, ( submission_id, tid, ) )
tp_file = r.fetchone()
tp_filename = do_decrypt( tp_file[ 'filename' ] )
tp_type = tp_file[ 'type' ]
sql = "SELECT name FROM pc WHERE id = %s"
pc_name = config.db.query( sql, ( pc, ) ).fetchone()[ 0 ]
sql = "SELECT gp.div_name FROM files_segments LEFT JOIN gp ON files_segments.gp = gp.id WHERE files_segments.tenprint = %s AND files_segments.pc = %s"
current_gp = config.db.query( sql, ( tid, pc, ) ).fetchone()[ 0 ]
if pc in xrange( 1, 10 ):
next_pc = pc + 1
elif pc == 25:
next_pc = 27
else:
next_pc = None
return render_template(
"submission/segment.html",
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
session_timeout = config.session_timeout,
submission_id = id,
nickname = nickname,
pc_name = pc_name,
tp_filename = tp_filename,
tid = tid,
pc = pc,
current_gp = current_gp,
tp_type = tp_type,
session_security_key = session.get( "session_security_key" )
)
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
@app.route( baseurl + '/submission/<id>/tenprint/<tid>/segment/<pc>/set/gp', methods = [ 'POST' ] )
@login_required
def submission_segment_set_gp( id, tid, pc ):
gp = request.form.get( "gp" )
sql = "SELECT id FROM gp WHERE name = %s"
r = config.db.query( sql, ( gp, ) ).fetchone()
if r == None:
return jsonify( {
'error': True,
'message': 'General patter not recognized'
} )
gp_id = r[ 'id' ]
sql = "UPDATE files_segments SET gp = %s WHERE tenprint = %s AND pc = %s"
config.db.query( sql, ( gp_id, tid, pc, ) )
config.db.commit()
return jsonify( {
'error': False
} )
################################################################################
# Tenprint templates
@app.route( baseurl + '/template/tenprint/list' )
@admin_required
def template_tenprint_list():
sql = "SELECT id, country_code, name FROM tenprint_cards"
tp_templates = config.db.query( sql ).fetchall()
return render_template(
"tp_template/list.html",
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
session_timeout = config.session_timeout,
tp_templates = tp_templates
)

Marco De Donno
committed
@app.route( baseurl + '/template/tenprint/new' )
@admin_required
def template_tenprint_new_meta():
return render_template(
"tp_template/new_meta.html",
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
session_timeout = config.session_timeout
)
@app.route( baseurl + '/template/tenprint/new/<id>/images' )
@admin_required
def template_tenprint_new_images( id ):
sql = "SELECT id, name, country_code FROM tenprint_cards WHERE id = %s"
card = config.db.query( sql, ( id, ) ).fetchone()
return render_template(
"tp_template/new_images.html",
baseurl = baseurl,
js = config.cdnjs,
css = config.cdncss,
session_timeout = config.session_timeout,
card = card
)

Marco De Donno
committed
@app.route( baseurl + '/template/tenprint/new/insert', methods = [ 'POST' ] )
@admin_required
def template_tenprint_new_do():
name = request.form.get( "name" )
country_code = request.form.get( "country_code" )
sql = "INSERT INTO tenprint_cards ( name, country_code ) VALUES ( %s, %s ) RETURNING id"
q = config.db.query( sql, ( name, country_code, ) )
id = q.fetchone()[ 0 ]

Marco De Donno
committed
for pc in [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 25, 27 ]:
sql = "INSERT INTO tenprint_zones ( card, pc, angle, tl_x, tl_y, br_x, br_y ) VALUES ( %s, %s, %s, %s, %s, %s, %s )"
config.db.query( sql, ( id, pc, 0, 0, 0, 0, 0 ) )

Marco De Donno
committed
config.db.commit()
return jsonify( {
'error': False,
'id': id
} )
@app.route( baseurl + '/template/tenprint/new/<id>/upload_image', methods = [ 'POST' ] )
@admin_required
def template_tenprint_new_uploadimage( id ):
face = request.form.get( "card_face" )
if face in [ "front", "back" ]:
data = request.files[ 'file' ]
img = Image.open( data )
image_width, image_height = img.size

Marco De Donno
committed
try:
res = img.info[ 'dpi' ][ 0 ]
width = round( image_width * 2.54 / float( res ) )
height = round( image_height * 2.54 / float( res ) )
except:
res = 0
width = 0
height = 0
fp = StringIO()
img.save( fp, format = "JPEG" )
fp.seek( 0 )
data = fp.getvalue()
data = base64.b64encode( data )
sql = """
UPDATE tenprint_cards
SET
image_{0} = %s,
image_{0}_width = %s,
image_{0}_height = %s,
image_resolution = %s,
image_format = %s,
width = %s,
height = %s
WHERE id = %s""".format( face )
config.db.query( sql, ( data, image_width, image_height, res, "JPEG", width, height, id, ) )
config.db.commit()

Marco De Donno
committed
if res != 0:
return jsonify( {
'error': False
} )
else:
return jsonify( {
'need_action': True,
'action': "set_resolution"
} )
else:
return abort( 403 )
@app.route( baseurl + '/template/tenprint/<id>/set/resolution', methods = [ 'POST' ] )

Marco De Donno
committed
@admin_required
def template_tenprint_new_setresolution( id ):
res = request.form.get( "resolution" )
try:
sql = "UPDATE tenprint_cards SET image_resolution = %s WHERE id = %s"
config.db.query( sql, ( res, id, ) )
config.db.commit()
return jsonify( {
'error': False
} )

Marco De Donno
committed
except:
return jsonify( {
'error': True
} )
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
def get_tenprint_template_zones( id, t ):
sql = """
SELECT
tenprint_zones.pc, tl_x, tl_y, br_x, br_y, angle, pc.name
FROM tenprint_zones
JOIN tenprint_zones_location ON tenprint_zones.pc = tenprint_zones_location.pc
JOIN pc ON tenprint_zones.pc = pc.id
WHERE
card = %s AND
tenprint_zones_location.side = %s
ORDER BY pc
"""
r = config.db.query( sql, ( id, t, ) ).fetchall()
zones = []
for pc, tl_x, tl_y, br_x, br_y, angle, pc_name in r:
tl_x = float_or_null( tl_x )
tl_y = float_or_null( tl_y )
br_x = float_or_null( br_x )
br_y = float_or_null( br_y )
zones.append( {
"pc": pc,
"tl_x": tl_x,
"tl_y": tl_y,
"br_x": br_x,
"br_y": br_y,
"angle": angle,
"pc_name": pc_name
} )
return zones
@app.route( baseurl + '/template/tenprint/<id>/<t>' )
@login_required
def tp_template( id, t ):
if t in [ 'front', 'back' ]:
sql = """SELECT
tenprint_zones.pc, tl_x, tl_y, br_x, br_y, angle, pc.name
FROM tenprint_zones
JOIN tenprint_zones_location ON tenprint_zones.pc = tenprint_zones_location.pc
JOIN pc ON tenprint_zones.pc = pc.id
WHERE card = %s AND tenprint_zones_location.side = %s ORDER BY pc
"""
r = config.db.query( sql, ( id, t, ) ).fetchall()
zones = []
for pc, tl_x, tl_y, br_x, br_y, angle, pc_name in r:
tl_x = float_or_null( tl_x )
tl_y = float_or_null( tl_y )
br_x = float_or_null( br_x )
br_y = float_or_null( br_y )
zones.append( {
"pc": pc,
"tl_x": tl_x,
"tl_y": tl_y,
"br_x": br_x,
"br_y": br_y,
"angle": angle,
"pc_name": pc_name
datacolumns = [ 'tl_x', 'tl_y', 'br_x', 'br_y', 'angle' ]

Marco De Donno
committed

Marco De Donno
committed
sql = 'SELECT id, name, country_code, width, height, size_display, image_' + t + '_width, image_' + t + '_height, image_resolution FROM tenprint_cards WHERE id = %s LIMIT 1'
r = config.db.query( sql, ( id, ) )
img_info = r.fetchone()
card_info = {
'width': int( round( float( img_info[ 'width' ] ) / 2.54 * img_info[ 'image_resolution' ] ) ),
'height': int( round( float( img_info[ 'height' ] ) / 2.54 * img_info[ 'image_resolution' ] ) ),
}
Loading
Loading full blame...