2018-05-04 13:53:29 +02:00
|
|
|
#!/usr/bin/env python3
|
2014-08-06 11:43:40 +02:00
|
|
|
# -*-coding:UTF-8 -*
|
|
|
|
|
2014-08-14 17:55:18 +02:00
|
|
|
import redis
|
2018-04-17 16:06:32 +02:00
|
|
|
import configparser
|
2019-05-02 17:31:14 +02:00
|
|
|
import random
|
2014-08-14 17:55:18 +02:00
|
|
|
import json
|
2016-07-21 13:44:22 +02:00
|
|
|
import datetime
|
2016-08-19 16:53:46 +02:00
|
|
|
import time
|
2016-08-13 15:24:57 +02:00
|
|
|
import calendar
|
2019-05-02 17:31:14 +02:00
|
|
|
from flask import Flask, render_template, jsonify, request, Request, session, redirect, url_for
|
|
|
|
from flask_login import LoginManager, current_user, login_user, logout_user, login_required
|
|
|
|
|
2019-05-03 16:52:05 +02:00
|
|
|
import bcrypt
|
|
|
|
|
2014-08-06 11:43:40 +02:00
|
|
|
import flask
|
2017-04-19 11:02:03 +02:00
|
|
|
import importlib
|
2014-08-26 17:33:28 +02:00
|
|
|
import os
|
2019-06-06 21:27:13 +02:00
|
|
|
import re
|
2017-04-19 11:02:03 +02:00
|
|
|
from os.path import join
|
2016-07-05 16:53:03 +02:00
|
|
|
import sys
|
|
|
|
sys.path.append(os.path.join(os.environ['AIL_BIN'], 'packages/'))
|
2017-04-19 11:02:03 +02:00
|
|
|
sys.path.append('./modules/')
|
2016-07-05 16:53:03 +02:00
|
|
|
import Paste
|
2016-07-21 13:44:22 +02:00
|
|
|
from Date import Date
|
2014-12-24 15:42:20 +01:00
|
|
|
|
2019-05-02 17:31:14 +02:00
|
|
|
from User import User
|
|
|
|
|
2018-05-23 16:58:56 +02:00
|
|
|
from pytaxonomies import Taxonomies
|
|
|
|
|
2016-12-09 08:46:37 +01:00
|
|
|
# Import config
|
|
|
|
import Flask_config
|
2016-08-19 13:34:02 +02:00
|
|
|
|
2019-05-03 16:52:05 +02:00
|
|
|
def flask_init():
|
|
|
|
# check if an account exists
|
2019-06-06 21:27:13 +02:00
|
|
|
if not r_serv_db.exists('user:all'):
|
|
|
|
password = secrets.token_urlsafe()
|
|
|
|
create_user_db('admin@admin.test', password, role='admin',default=True)
|
|
|
|
# add default roles
|
|
|
|
if not r_serv_db.exists('ail:all_role'):
|
|
|
|
r_serv_db.zadd('ail:all_role', 1, 'admin')
|
|
|
|
r_serv_db.zadd('ail:all_role', 2, 'analyst')
|
2019-05-03 16:52:05 +02:00
|
|
|
|
|
|
|
def hashing_password(bytes_password):
|
|
|
|
hashed = bcrypt.hashpw(bytes_password, bcrypt.gensalt())
|
|
|
|
return hashed
|
|
|
|
|
|
|
|
def verify_password(id, bytes_password):
|
|
|
|
hashed_password = r_serv_db.hget('user:all', id)
|
|
|
|
if bcrypt.checkpw(password, hashed):
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
2019-06-06 21:27:13 +02:00
|
|
|
def check_password_strength(password):
|
|
|
|
result = regex_password.match(password)
|
|
|
|
if result:
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
2019-05-03 16:52:05 +02:00
|
|
|
|
|
|
|
|
2019-06-06 21:27:13 +02:00
|
|
|
def create_user_db(username_id , password, default=False, role=None, update=False):
|
2019-05-03 16:52:05 +02:00
|
|
|
password = password.encode()
|
|
|
|
password_hash = hashing_password(password)
|
|
|
|
r_serv_db.hset('user:all', username_id, password_hash)
|
2019-06-06 21:27:13 +02:00
|
|
|
if update:
|
|
|
|
r_serv_db.hdel('user_metadata:{}'.format(username_id), 'change_passwd')
|
|
|
|
if username_id=='admin@admin.test':
|
|
|
|
os.remove(default_passwd_file)
|
|
|
|
else:
|
|
|
|
if default:
|
|
|
|
r_serv_db.hset('user_metadata:{}'.format(username_id), 'change_passwd', True)
|
|
|
|
if role:
|
|
|
|
if role in get_all_role():
|
|
|
|
r_serv_db.sadd('user_role:{}'.format(role), username_id)
|
|
|
|
|
|
|
|
def get_all_role():
|
|
|
|
return r_serv_db.zrange('ail:all_role', 0 , -1)
|
2019-05-03 16:52:05 +02:00
|
|
|
|
2016-12-09 08:46:37 +01:00
|
|
|
# CONFIG #
|
|
|
|
cfg = Flask_config.cfg
|
2018-09-20 10:38:19 +02:00
|
|
|
baseUrl = cfg.get("Flask", "baseurl")
|
|
|
|
baseUrl = baseUrl.replace('/', '')
|
|
|
|
if baseUrl != '':
|
|
|
|
baseUrl = '/'+baseUrl
|
2016-08-24 18:00:05 +02:00
|
|
|
|
2019-06-06 21:27:13 +02:00
|
|
|
default_passwd_file = os.path.join(os.environ['AIL_HOME'], 'DEFAULT_PASSWORD')
|
|
|
|
|
|
|
|
regex_password = r'^(?=(.*\d){2})(?=.*[a-z])(?=.*[A-Z]).{10,}$'
|
|
|
|
regex_password = re.compile(regex_password)
|
|
|
|
|
2019-05-03 16:52:05 +02:00
|
|
|
# ========= REDIS =========#
|
|
|
|
r_serv_db = redis.StrictRedis(
|
|
|
|
host=cfg.get("ARDB_DB", "host"),
|
|
|
|
port=cfg.getint("ARDB_DB", "port"),
|
|
|
|
db=cfg.getint("ARDB_DB", "db"),
|
|
|
|
decode_responses=True)
|
|
|
|
r_serv_tags = redis.StrictRedis(
|
|
|
|
host=cfg.get("ARDB_Tags", "host"),
|
|
|
|
port=cfg.getint("ARDB_Tags", "port"),
|
|
|
|
db=cfg.getint("ARDB_Tags", "db"),
|
|
|
|
decode_responses=True)
|
|
|
|
|
|
|
|
# ========= =========#
|
|
|
|
|
2018-09-20 10:38:19 +02:00
|
|
|
Flask_config.app = Flask(__name__, static_url_path=baseUrl+'/static/')
|
2016-12-09 08:46:37 +01:00
|
|
|
app = Flask_config.app
|
2018-06-08 16:49:20 +02:00
|
|
|
app.config['MAX_CONTENT_LENGTH'] = 900 * 1024 * 1024
|
2014-08-26 17:33:28 +02:00
|
|
|
|
2019-05-02 17:31:14 +02:00
|
|
|
# ========= session ========
|
|
|
|
app.secret_key = str(random.getrandbits(256))
|
|
|
|
login_manager = LoginManager()
|
|
|
|
login_manager.login_view = 'login'
|
|
|
|
login_manager.init_app(app)
|
|
|
|
|
|
|
|
# ========= LOGIN MANAGER ========
|
|
|
|
|
|
|
|
@login_manager.user_loader
|
|
|
|
def load_user(user_id):
|
|
|
|
return User.get(user_id)
|
|
|
|
|
2017-04-25 12:18:08 +02:00
|
|
|
# ========= HEADER GENERATION ========
|
|
|
|
|
|
|
|
# Get headers items that should be ignored (not displayed)
|
|
|
|
toIgnoreModule = set()
|
|
|
|
try:
|
|
|
|
with open('templates/ignored_modules.txt', 'r') as f:
|
|
|
|
lines = f.read().splitlines()
|
|
|
|
for line in lines:
|
|
|
|
toIgnoreModule.add(line)
|
|
|
|
|
|
|
|
except IOError:
|
|
|
|
f = open('templates/ignored_modules.txt', 'w')
|
|
|
|
f.close()
|
|
|
|
|
2017-04-19 15:14:20 +02:00
|
|
|
# Dynamically import routes and functions from modules
|
|
|
|
# Also, prepare header.html
|
|
|
|
to_add_to_header_dico = {}
|
2017-04-19 11:02:03 +02:00
|
|
|
for root, dirs, files in os.walk('modules/'):
|
2017-04-19 15:14:20 +02:00
|
|
|
sys.path.append(join(root))
|
2017-04-25 12:18:08 +02:00
|
|
|
|
|
|
|
# Ignore the module
|
|
|
|
curr_dir = root.split('/')[1]
|
|
|
|
if curr_dir in toIgnoreModule:
|
|
|
|
continue
|
|
|
|
|
2017-04-19 15:14:20 +02:00
|
|
|
for name in files:
|
|
|
|
module_name = root.split('/')[-2]
|
|
|
|
if name.startswith('Flask_') and name.endswith('.py'):
|
|
|
|
if name == 'Flask_config.py':
|
|
|
|
continue
|
|
|
|
name = name.strip('.py')
|
|
|
|
#print('importing {}'.format(name))
|
|
|
|
importlib.import_module(name)
|
|
|
|
elif name == 'header_{}.html'.format(module_name):
|
|
|
|
with open(join(root, name), 'r') as f:
|
|
|
|
to_add_to_header_dico[module_name] = f.read()
|
2014-08-26 17:33:28 +02:00
|
|
|
|
2017-04-19 15:14:20 +02:00
|
|
|
#create header.html
|
|
|
|
complete_header = ""
|
|
|
|
with open('templates/header_base.html', 'r') as f:
|
|
|
|
complete_header = f.read()
|
|
|
|
modified_header = complete_header
|
|
|
|
|
|
|
|
#Add the header in the supplied order
|
2018-04-17 16:06:32 +02:00
|
|
|
for module_name, txt in list(to_add_to_header_dico.items()):
|
2017-04-19 15:14:20 +02:00
|
|
|
to_replace = '<!--{}-->'.format(module_name)
|
|
|
|
if to_replace in complete_header:
|
|
|
|
modified_header = modified_header.replace(to_replace, txt)
|
|
|
|
del to_add_to_header_dico[module_name]
|
|
|
|
|
|
|
|
#Add the header for no-supplied order
|
|
|
|
to_add_to_header = []
|
|
|
|
for module_name, txt in to_add_to_header_dico.items():
|
|
|
|
to_add_to_header.append(txt)
|
|
|
|
|
|
|
|
modified_header = modified_header.replace('<!--insert here-->', '\n'.join(to_add_to_header))
|
|
|
|
|
|
|
|
#Write the header.html file
|
|
|
|
with open('templates/header.html', 'w') as f:
|
|
|
|
f.write(modified_header)
|
|
|
|
|
2019-06-06 21:27:13 +02:00
|
|
|
flask_init()
|
|
|
|
|
2017-04-19 15:14:20 +02:00
|
|
|
|
|
|
|
# ========= JINJA2 FUNCTIONS ========
|
2016-07-05 16:53:03 +02:00
|
|
|
def list_len(s):
|
|
|
|
return len(s)
|
|
|
|
app.jinja_env.filters['list_len'] = list_len
|
|
|
|
|
2016-08-04 11:55:38 +02:00
|
|
|
|
2016-08-09 11:59:36 +02:00
|
|
|
# ========= CACHE CONTROL ========
|
|
|
|
@app.after_request
|
|
|
|
def add_header(response):
|
|
|
|
"""
|
|
|
|
Add headers to both force latest IE rendering engine or Chrome Frame,
|
|
|
|
and also to cache the rendered page for 10 minutes.
|
|
|
|
"""
|
|
|
|
response.headers['X-UA-Compatible'] = 'IE=Edge,chrome=1'
|
|
|
|
response.headers['Cache-Control'] = 'public, max-age=0'
|
|
|
|
return response
|
2016-07-21 13:44:22 +02:00
|
|
|
|
2017-04-19 15:14:20 +02:00
|
|
|
# ========== ROUTES ============
|
2019-05-02 17:31:14 +02:00
|
|
|
@app.route('/login', methods=['POST', 'GET'])
|
|
|
|
def login():
|
|
|
|
if request.method == 'POST':
|
|
|
|
username = request.form.get('username')
|
|
|
|
password = request.form.get('password')
|
2019-06-06 21:27:13 +02:00
|
|
|
#next_page = request.form.get('next_page')
|
2019-05-02 17:31:14 +02:00
|
|
|
|
|
|
|
if username is not None:
|
|
|
|
user = User.get(username)
|
|
|
|
if user and user.check_password(password):
|
|
|
|
login_user(user) ## TODO: use remember me ?
|
2019-05-03 16:52:05 +02:00
|
|
|
print(user.is_active)
|
2019-06-06 21:27:13 +02:00
|
|
|
if user.request_password_change():
|
|
|
|
return redirect(url_for('change_password'))
|
|
|
|
else:
|
|
|
|
return redirect(url_for('dashboard.index'))
|
2019-05-02 17:31:14 +02:00
|
|
|
else:
|
|
|
|
return 'incorrect password'
|
|
|
|
|
|
|
|
return 'none'
|
|
|
|
|
|
|
|
else:
|
2019-06-06 21:27:13 +02:00
|
|
|
#next_page = request.args.get('next')
|
|
|
|
return render_template("login.html")
|
|
|
|
|
|
|
|
@app.route('/change_password', methods=['POST', 'GET'])
|
|
|
|
@login_required
|
|
|
|
def change_password():
|
|
|
|
password1 = request.form.get('password1')
|
|
|
|
password2 = request.form.get('password2')
|
|
|
|
|
|
|
|
# # TODO: display errors message
|
|
|
|
|
|
|
|
if current_user.is_authenticated and password1!=None and password1==password2:
|
|
|
|
if check_password_strength(password1):
|
|
|
|
user_id = current_user.get_id()
|
|
|
|
create_user_db(user_id , password1, update=True)
|
|
|
|
return redirect(url_for('dashboard.index'))
|
|
|
|
else:
|
|
|
|
return render_template("change_password.html")
|
|
|
|
else:
|
|
|
|
return render_template("change_password.html")
|
2019-05-02 17:31:14 +02:00
|
|
|
|
2019-05-06 16:58:36 +02:00
|
|
|
@app.route('/role', methods=['POST', 'GET'])
|
|
|
|
def role():
|
|
|
|
return 'ERROR role'
|
|
|
|
|
2019-05-02 17:31:14 +02:00
|
|
|
@app.route('/logout')
|
|
|
|
@login_required
|
|
|
|
def logout():
|
|
|
|
logout_user()
|
2019-05-03 16:52:05 +02:00
|
|
|
return redirect(url_for('login'))
|
|
|
|
|
|
|
|
@app.route('/create_user')
|
|
|
|
@login_required
|
|
|
|
def create_user():
|
|
|
|
username = request.form.get('username')
|
|
|
|
password = request.form.get('password')
|
|
|
|
#role = request.form.get('role') ## TODO: create role
|
|
|
|
|
|
|
|
## TODO: validate username
|
|
|
|
## TODO: validate password
|
|
|
|
|
|
|
|
username = 'admin@admin.test'
|
|
|
|
password = 'admin'
|
|
|
|
|
|
|
|
if r_serv_db.hexists('user:all', username):
|
|
|
|
return 'this id is not available'
|
|
|
|
|
|
|
|
create_user_db(username, password)
|
|
|
|
|
|
|
|
return 'True'
|
2019-05-02 17:31:14 +02:00
|
|
|
|
|
|
|
|
2017-04-19 15:14:20 +02:00
|
|
|
@app.route('/searchbox/')
|
|
|
|
def searchbox():
|
|
|
|
return render_template("searchbox.html")
|
|
|
|
|
|
|
|
|
2018-05-23 16:58:56 +02:00
|
|
|
# ========== INITIAL taxonomies ============
|
|
|
|
# add default ail taxonomies
|
|
|
|
r_serv_tags.sadd('active_taxonomies', 'infoleak')
|
2018-05-30 16:18:58 +02:00
|
|
|
r_serv_tags.sadd('active_taxonomies', 'gdpr')
|
|
|
|
r_serv_tags.sadd('active_taxonomies', 'fpf')
|
2018-05-23 16:58:56 +02:00
|
|
|
# add default tags
|
|
|
|
taxonomies = Taxonomies()
|
|
|
|
for tag in taxonomies.get('infoleak').machinetags():
|
|
|
|
r_serv_tags.sadd('active_tag_infoleak', tag)
|
2018-05-30 16:18:58 +02:00
|
|
|
for tag in taxonomies.get('gdpr').machinetags():
|
2018-06-06 10:05:25 +02:00
|
|
|
r_serv_tags.sadd('active_tag_gdpr', tag)
|
2018-05-30 16:18:58 +02:00
|
|
|
for tag in taxonomies.get('fpf').machinetags():
|
2018-06-06 10:05:25 +02:00
|
|
|
r_serv_tags.sadd('active_tag_fpf', tag)
|
2018-05-23 16:58:56 +02:00
|
|
|
|
2018-06-15 17:25:43 +02:00
|
|
|
# ========== INITIAL tags auto export ============
|
|
|
|
infoleak_tags = taxonomies.get('infoleak').machinetags()
|
|
|
|
infoleak_automatic_tags = []
|
|
|
|
for tag in taxonomies.get('infoleak').machinetags():
|
|
|
|
if tag.split('=')[0][:] == 'infoleak:automatic-detection':
|
|
|
|
r_serv_db.sadd('list_export_tags', tag)
|
|
|
|
|
2018-06-19 16:39:49 +02:00
|
|
|
r_serv_db.sadd('list_export_tags', 'infoleak:submission="manual"')
|
2016-12-09 08:46:37 +01:00
|
|
|
# ============ MAIN ============
|
2016-07-08 10:19:24 +02:00
|
|
|
|
2014-08-06 11:43:40 +02:00
|
|
|
if __name__ == "__main__":
|
2014-08-14 17:55:18 +02:00
|
|
|
app.run(host='0.0.0.0', port=7000, threaded=True)
|