mirror of https://github.com/CIRCL/AIL-framework
				
				
				
			
		
			
				
	
	
		
			302 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			302 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Python
		
	
	
| #!/usr/bin/env python3
 | |
| # -*-coding:UTF-8 -*
 | |
| 
 | |
| """
 | |
|     Flask functions and routes for the Item Submit modules page
 | |
| """
 | |
| ##################################
 | |
| # Import External packages
 | |
| ##################################
 | |
| import re
 | |
| import os
 | |
| import sys
 | |
| import string
 | |
| import unicodedata
 | |
| import uuid
 | |
| 
 | |
| from functools import wraps
 | |
| 
 | |
| # Flask
 | |
| from flask import render_template, jsonify, request, Blueprint, url_for, redirect, abort
 | |
| from Role_Manager import login_admin, login_user_no_api
 | |
| from flask_login import login_required
 | |
| 
 | |
| 
 | |
| ##################################
 | |
| # Import Project packages
 | |
| ##################################
 | |
| from lib import Tag
 | |
| 
 | |
| from packages import Import_helper
 | |
| 
 | |
| 
 | |
| # ============ VARIABLES ============
 | |
| import Flask_config
 | |
| 
 | |
| app = Flask_config.app
 | |
| baseUrl = Flask_config.baseUrl
 | |
| 
 | |
| r_serv_db = Flask_config.r_serv_db # TODO REMOVE ME
 | |
| r_serv_log_submit = Flask_config.r_serv_log_submit # TODO REMOVE ME
 | |
| 
 | |
| logger = Flask_config.redis_logger
 | |
| 
 | |
| 
 | |
| valid_filename_chars = "-_ %s%s" % (string.ascii_letters, string.digits)
 | |
| 
 | |
| UPLOAD_FOLDER = Flask_config.UPLOAD_FOLDER
 | |
| 
 | |
| text_max_size = int(Flask_config.SUBMIT_PASTE_TEXT_MAX_SIZE) / (1000*1000)
 | |
| file_max_size = int(Flask_config.SUBMIT_PASTE_FILE_MAX_SIZE) / (1000*1000*1000)
 | |
| allowed_extensions = ", ". join(Flask_config.SUBMIT_PASTE_FILE_ALLOWED_EXTENSIONS)
 | |
| 
 | |
| PasteSubmit = Blueprint('PasteSubmit', __name__, template_folder='templates')
 | |
| 
 | |
| # ============ Validators ============
 | |
| def limit_content_length():
 | |
|     def decorator(f):
 | |
|         @wraps(f)
 | |
|         def wrapper(*args, **kwargs):
 | |
|             logger.debug('decorator')
 | |
|             cl = request.content_length
 | |
|             if cl is not None:
 | |
|                 if cl > Flask_config.SUBMIT_PASTE_FILE_MAX_SIZE or ('file' not in request.files and cl > Flask_config.SUBMIT_PASTE_TEXT_MAX_SIZE):
 | |
|                     logger.debug('abort')
 | |
|                     abort(413)
 | |
|             return f(*args, **kwargs)
 | |
|         return wrapper
 | |
|     return decorator
 | |
| 
 | |
| 
 | |
| # ============ FUNCTIONS ============
 | |
| 
 | |
| def allowed_file(filename):
 | |
|     if '.' not in filename:
 | |
|         return True
 | |
|     else:
 | |
|         file_ext = filename.rsplit('.', 1)[1].lower()
 | |
|         logger.debug(file_ext)
 | |
|         return file_ext in Flask_config.SUBMIT_PASTE_FILE_ALLOWED_EXTENSIONS
 | |
| 
 | |
| def clean_filename(filename, whitelist=valid_filename_chars, replace=' '):
 | |
|     # replace characters
 | |
|     for r in replace:
 | |
|         filename = filename.replace(r, '_')
 | |
| 
 | |
|     # keep only valid ascii chars
 | |
|     cleaned_filename = unicodedata.normalize('NFKD', filename).encode('ASCII', 'ignore').decode()
 | |
| 
 | |
|     # keep only whitelisted chars
 | |
|     return ''.join(c for c in cleaned_filename if c in whitelist)
 | |
| 
 | |
| # ============= ROUTES ==============
 | |
| 
 | |
| @PasteSubmit.route("/PasteSubmit/", methods=['GET'])
 | |
| @login_required
 | |
| @login_user_no_api
 | |
| def PasteSubmit_page():
 | |
|     # Get all active tags/galaxy
 | |
|     active_taxonomies = Tag.get_active_taxonomies()
 | |
|     active_galaxies = Tag.get_active_galaxies()
 | |
| 
 | |
|     return render_template("submit_items.html",
 | |
|                             active_taxonomies = active_taxonomies,
 | |
|                             active_galaxies = active_galaxies,
 | |
|                             text_max_size = text_max_size,
 | |
|                             file_max_size = file_max_size,
 | |
|                             allowed_extensions = allowed_extensions)
 | |
| 
 | |
| @PasteSubmit.route("/PasteSubmit/submit", methods=['POST'])
 | |
| @login_required
 | |
| @login_user_no_api
 | |
| @limit_content_length()
 | |
| def submit():
 | |
|     logger.debug('submit')
 | |
| 
 | |
|     password = request.form['archive_pass']
 | |
|     ltags = request.form['tags_taxonomies']
 | |
|     ltagsgalaxies = request.form['tags_galaxies']
 | |
|     paste_content = request.form['paste_content']
 | |
|     paste_source = request.form['paste_source']
 | |
| 
 | |
|     if paste_source:
 | |
|         # limit source length
 | |
|         paste_source = paste_source.replace('/', '')[:80]
 | |
|         if paste_source in ['crawled', 'tests']:
 | |
|             content = 'Invalid source'
 | |
|             logger.info(paste_source)
 | |
|             return content, 400
 | |
| 
 | |
|         if not re.match('^[0-9a-zA-Z-_\+@#&\.;=:!]*$', paste_source):
 | |
|             content = f'Invalid source name: Forbidden character(s)'
 | |
|             logger.info(content)
 | |
|             return content, 400
 | |
| 
 | |
|     is_file = False
 | |
|     if 'file' in request.files:
 | |
|         file_import = request.files['file']
 | |
|         if file_import:
 | |
|             if file_import.filename:
 | |
|                 is_file = True
 | |
| 
 | |
|     logger.debug(f'is file ? {is_file}')
 | |
| 
 | |
|     submitted_tag = 'infoleak:submission="manual"'
 | |
| 
 | |
|     # active taxonomies
 | |
|     active_taxonomies = Tag.get_active_taxonomies()
 | |
|     # active galaxies
 | |
|     active_galaxies = Tag.get_active_galaxies()
 | |
| 
 | |
|     if ltags or ltagsgalaxies:
 | |
|         logger.debug(f'ltags ? {ltags} {ltagsgalaxies}')
 | |
|         ltags = Tag.unpack_str_tags_list(ltags)
 | |
|         ltagsgalaxies = Tag.unpack_str_tags_list(ltagsgalaxies)
 | |
| 
 | |
|         if not Tag.is_valid_tags_taxonomies_galaxy(ltags, ltagsgalaxies):
 | |
|             content = 'INVALID TAGS'
 | |
|             logger.info(content)
 | |
|             return content, 400
 | |
| 
 | |
|     # add submitted tags
 | |
|     if not ltags:
 | |
|         ltags = []
 | |
|     ltags.append(submitted_tag)
 | |
| 
 | |
|     if is_file:
 | |
|         logger.debug('file management')
 | |
| 
 | |
|         if allowed_file(file_import.filename):
 | |
|             logger.debug('file extension allowed')
 | |
| 
 | |
|             # get UUID
 | |
|             UUID = str(uuid.uuid4())
 | |
| 
 | |
|             # create submitted dir
 | |
|             if not os.path.exists(UPLOAD_FOLDER):
 | |
|                 logger.debug('create folder')
 | |
|                 os.makedirs(UPLOAD_FOLDER)
 | |
| 
 | |
|             if '.' not in file_import.filename:
 | |
|                 logger.debug('add UUID to path')
 | |
|                 full_path = os.path.join(UPLOAD_FOLDER, UUID)
 | |
|             else:
 | |
|                 if file_import.filename[-6:] == 'tar.gz':
 | |
|                     logger.debug('file extension is tar.gz')
 | |
|                     file_type = 'tar.gz'
 | |
|                 else:
 | |
|                     file_type = file_import.filename.rsplit('.', 1)[1]
 | |
|                     logger.debug(f'file type {file_type}')
 | |
|                 name = UUID + '.' + file_type
 | |
|                 full_path = os.path.join(UPLOAD_FOLDER, name)
 | |
|                 logger.debug(f'full path {full_path}')
 | |
| 
 | |
|             # Flask verify the file size
 | |
|             file_import.save(full_path)
 | |
|             logger.debug('file saved')
 | |
| 
 | |
|             Import_helper.create_import_queue(ltags, ltagsgalaxies, full_path, UUID, password, True)
 | |
| 
 | |
|             return render_template("submit_items.html",
 | |
|                                    active_taxonomies=active_taxonomies,
 | |
|                                    active_galaxies=active_galaxies,
 | |
|                                    UUID=UUID)
 | |
| 
 | |
|         else:
 | |
|             content = f'wrong file type, allowed_extensions: {allowed_extensions} or remove the extension'
 | |
|             logger.info(content)
 | |
|             return content, 400
 | |
| 
 | |
|     elif paste_content != '':
 | |
|         logger.debug(f'entering text paste management')
 | |
|         if sys.getsizeof(paste_content) < Flask_config.SUBMIT_PASTE_TEXT_MAX_SIZE:
 | |
|             logger.debug(f'size {sys.getsizeof(paste_content)}')
 | |
|             # get id
 | |
|             UUID = str(uuid.uuid4())
 | |
|             logger.debug('create import')
 | |
|             Import_helper.create_import_queue(ltags, ltagsgalaxies, paste_content, UUID, password, source=paste_source)
 | |
|             logger.debug('import OK')
 | |
|             return render_template("submit_items.html",
 | |
|                                         active_taxonomies = active_taxonomies,
 | |
|                                         active_galaxies = active_galaxies,
 | |
|                                         UUID = UUID)
 | |
| 
 | |
|         else:
 | |
|             content = f'text paste size is over {Flask_config.SUBMIT_PASTE_TEXT_MAX_SIZE} bytes limit'
 | |
|             logger.info(content)
 | |
|             return content, 400
 | |
| 
 | |
|         content = 'submit aborded'
 | |
|         logger.error(content)
 | |
|         return content, 400
 | |
| 
 | |
|     return PasteSubmit_page()
 | |
| 
 | |
| @PasteSubmit.route("/PasteSubmit/submit_status", methods=['GET'])
 | |
| @login_required
 | |
| @login_user_no_api
 | |
| def submit_status():
 | |
|     UUID = request.args.get('UUID')
 | |
| 
 | |
|     if UUID:
 | |
|         end = r_serv_log_submit.get(UUID + ':end')
 | |
|         nb_total = r_serv_log_submit.get(UUID + ':nb_total')
 | |
|         nb_end = r_serv_log_submit.get(UUID + ':nb_end')
 | |
|         error = r_serv_log_submit.get(UUID + ':error')
 | |
|         processing = r_serv_log_submit.get(UUID + ':processing')
 | |
|         nb_sucess = r_serv_log_submit.get(UUID + ':nb_sucess')
 | |
|         paste_submit_link = list(r_serv_log_submit.smembers(UUID + ':paste_submit_link'))
 | |
| 
 | |
|         if (end != None) and (nb_total != None) and (nb_end != None) and (processing != None):
 | |
| 
 | |
|             link = ''
 | |
|             if paste_submit_link:
 | |
|                 for paste in paste_submit_link:
 | |
|                     url = url_for('objects_item.showItem') + '?id=' + paste
 | |
|                     link += '<a target="_blank" href="' + url + '" class="list-group-item">' + paste +'</a>'
 | |
| 
 | |
|             if nb_total == '-1':
 | |
|                 in_progress = nb_sucess + ' / '
 | |
|             else:
 | |
|                 in_progress = nb_sucess + ' / ' + nb_total
 | |
| 
 | |
|             if int(nb_total) != 0:
 | |
|                 prog = int(int(nb_end) * 100 / int(nb_total))
 | |
|             else:
 | |
|                 prog = 0
 | |
| 
 | |
|             isError = bool(error)
 | |
| 
 | |
|             if end == '0':
 | |
|                 end = False
 | |
|             else:
 | |
|                 end = True
 | |
| 
 | |
|             if processing == '0':
 | |
|                 processing = False
 | |
|             else:
 | |
|                 processing = True
 | |
| 
 | |
|             return jsonify(end=end,
 | |
|                             in_progress=in_progress,
 | |
|                             prog=prog,
 | |
|                             link=link,
 | |
|                             processing=processing,
 | |
|                             isError=isError,
 | |
|                             error=error)
 | |
|         else:
 | |
|             # FIXME TODO
 | |
|             print(end)
 | |
|             print(nb_total)
 | |
|             print(nb_end)
 | |
|             print(error)
 | |
|             print(processing)
 | |
|             print(nb_sucess)
 | |
|             return 'to do'
 | |
|     else:
 | |
|         return 'INVALID UUID'
 | |
| 
 | |
| 
 | |
| # ========= REGISTRATION =========
 | |
| app.register_blueprint(PasteSubmit, url_prefix=baseUrl)
 |