2024-02-26 15:35:48 +01:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# -*-coding:UTF-8 -*
|
|
|
|
|
|
|
|
"""
|
|
|
|
Blueprint Flask: crawler splash endpoints: dashboard, onion crawler ...
|
|
|
|
"""
|
|
|
|
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import json
|
|
|
|
|
|
|
|
from functools import wraps
|
|
|
|
from flask import request, Blueprint, Response
|
|
|
|
|
|
|
|
sys.path.append(os.environ['AIL_BIN'])
|
|
|
|
##################################
|
|
|
|
# Import Project packages
|
|
|
|
##################################
|
|
|
|
from lib import ail_api
|
|
|
|
from lib import ail_core
|
|
|
|
from lib import ail_updates
|
2024-08-27 15:48:11 +02:00
|
|
|
from lib import ail_logger
|
2024-02-26 15:35:48 +01:00
|
|
|
from lib import crawlers
|
2024-02-29 16:18:22 +01:00
|
|
|
from lib import chats_viewer
|
2024-02-26 15:35:48 +01:00
|
|
|
|
2024-02-29 14:56:45 +01:00
|
|
|
from lib import Investigations
|
2024-02-26 15:35:48 +01:00
|
|
|
from lib import Tag
|
|
|
|
|
|
|
|
from lib.objects import ail_objects
|
|
|
|
from lib.objects import Domains
|
|
|
|
from lib.objects import Titles
|
|
|
|
|
2024-02-29 14:56:45 +01:00
|
|
|
from importer.FeederImporter import api_add_json_feeder_to_queue
|
2024-02-26 15:35:48 +01:00
|
|
|
|
|
|
|
|
2024-08-27 15:48:11 +02:00
|
|
|
# LOGS
|
|
|
|
# access_logger = ail_logger.get_access_config()
|
|
|
|
|
|
|
|
|
2024-02-26 15:35:48 +01:00
|
|
|
# ============ BLUEPRINT ============
|
|
|
|
api_rest = Blueprint('api_rest', __name__, template_folder=os.path.join(os.environ['AIL_FLASK'], 'templates'))
|
|
|
|
|
|
|
|
|
|
|
|
# ============ AUTH FUNCTIONS ============
|
|
|
|
|
|
|
|
def get_auth_from_header():
|
|
|
|
token = request.headers.get('Authorization').replace(' ', '') # remove space
|
|
|
|
return token
|
|
|
|
|
|
|
|
|
|
|
|
def token_required(user_role):
|
|
|
|
def actual_decorator(funct):
|
|
|
|
@wraps(funct)
|
|
|
|
def api_token(*args, **kwargs):
|
|
|
|
# Check AUTH Header
|
|
|
|
if not request.headers.get('Authorization'):
|
|
|
|
return create_json_response({'status': 'error', 'reason': 'Authentication needed'}, 401)
|
|
|
|
|
|
|
|
# Check Role
|
|
|
|
if not user_role:
|
|
|
|
return create_json_response({'status': 'error', 'reason': 'Invalid Role'}, 401)
|
|
|
|
|
|
|
|
token = get_auth_from_header()
|
2024-09-06 11:16:52 +02:00
|
|
|
ip_source = request.access_route[0]
|
2024-02-26 15:35:48 +01:00
|
|
|
data, status_code = ail_api.authenticate_user(token, ip_address=ip_source)
|
|
|
|
if status_code != 200:
|
|
|
|
return create_json_response(data, status_code)
|
|
|
|
elif data:
|
|
|
|
# check user role
|
|
|
|
if not ail_api.is_user_in_role(user_role, token):
|
|
|
|
return create_json_response({'status': 'error', 'reason': 'Access Forbidden'}, 403)
|
|
|
|
else:
|
|
|
|
# User Authenticated + In Role
|
2024-08-27 15:48:11 +02:00
|
|
|
# print(funct.__name__)
|
2024-02-26 15:35:48 +01:00
|
|
|
return funct(*args, **kwargs)
|
|
|
|
else:
|
|
|
|
return create_json_response({'status': 'error', 'reason': 'Internal'}, 400)
|
|
|
|
|
|
|
|
return api_token
|
|
|
|
return actual_decorator
|
|
|
|
|
|
|
|
|
|
|
|
# ============ FUNCTIONS ============
|
|
|
|
|
2024-02-29 14:56:45 +01:00
|
|
|
def create_json_response(data, status_code):
|
|
|
|
return Response(json.dumps(data) + "\n", mimetype='application/json'), status_code
|
2024-02-26 15:35:48 +01:00
|
|
|
|
|
|
|
# ============= ROUTES ==============
|
|
|
|
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
# # # # # # # # # # # # # # # CORE # # # # # # # # # # # # # # # # # #
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
|
|
|
|
@api_rest.route("api/v1/ping", methods=['GET'])
|
2024-09-05 16:40:24 +02:00
|
|
|
@token_required('user')
|
2024-02-26 15:35:48 +01:00
|
|
|
def v1_ping():
|
|
|
|
return create_json_response({'status': 'pong'}, 200)
|
|
|
|
|
|
|
|
@api_rest.route("api/v1/uuid", methods=['GET'])
|
2024-09-05 16:40:24 +02:00
|
|
|
@token_required('user')
|
2024-02-26 15:35:48 +01:00
|
|
|
def v1_uuid():
|
|
|
|
ail_uid = ail_core.get_ail_uuid()
|
|
|
|
return create_json_response({'uuid': ail_uid}, 200)
|
|
|
|
|
|
|
|
@api_rest.route("api/v1/version", methods=['GET'])
|
2024-09-05 16:40:24 +02:00
|
|
|
@token_required('user')
|
2024-02-26 15:35:48 +01:00
|
|
|
def v1_version():
|
|
|
|
version = ail_updates.get_ail_version()
|
|
|
|
return create_json_response({'version': version}, 200)
|
|
|
|
|
|
|
|
@api_rest.route("api/v1/pyail/version", methods=['GET'])
|
2024-09-05 16:40:24 +02:00
|
|
|
@token_required('user')
|
2024-02-26 15:35:48 +01:00
|
|
|
def v1_pyail_version():
|
|
|
|
ail_version = 'v1.0.0'
|
|
|
|
return create_json_response({'version': ail_version}, 200)
|
|
|
|
|
|
|
|
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
# # # # # # # # # # # # # # # CRAWLERS # # # # # # # # # # # # # # # # #
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
# # TODO: ADD RESULT JSON Response
|
|
|
|
@api_rest.route("api/v1/add/crawler/task", methods=['POST']) # TODO V2 Migration
|
2024-09-05 16:40:24 +02:00
|
|
|
@token_required('user')
|
2024-02-26 15:35:48 +01:00
|
|
|
def add_crawler_task():
|
|
|
|
data = request.get_json()
|
|
|
|
user_token = get_auth_from_header()
|
2024-08-28 14:32:26 +02:00
|
|
|
user_org, user_id, _ = get_basic_user_meta(token)
|
|
|
|
res = crawlers.api_add_crawler_task(data, user_org, user_id=user_id)
|
2024-02-26 15:35:48 +01:00
|
|
|
if res:
|
|
|
|
return create_json_response(res[0], res[1])
|
|
|
|
|
|
|
|
dict_res = {'url': data['url']}
|
|
|
|
return create_json_response(dict_res, 200)
|
|
|
|
|
|
|
|
|
|
|
|
@api_rest.route("api/v1/add/crawler/capture", methods=['POST']) # TODO V2 Migration
|
2024-09-05 16:40:24 +02:00
|
|
|
@token_required('user')
|
2024-02-26 15:35:48 +01:00
|
|
|
def add_crawler_capture():
|
|
|
|
data = request.get_json()
|
|
|
|
user_token = get_auth_from_header()
|
|
|
|
user_id = ail_api.get_user_from_token(user_token)
|
|
|
|
res = crawlers.api_add_crawler_capture(data, user_id)
|
|
|
|
if res:
|
|
|
|
return create_json_response(res[0], res[1])
|
|
|
|
|
|
|
|
dict_res = {'url': data['url']}
|
|
|
|
return create_json_response(dict_res, 200)
|
|
|
|
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
# # # # # # # # # # # # # # IMPORTERS # # # # # # # # # # # # # # # # #
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
@api_rest.route("api/v1/import/json/item", methods=['POST']) # TODO V2 Migration
|
|
|
|
@token_required('user')
|
|
|
|
def import_json_item():
|
|
|
|
data_json = request.get_json()
|
|
|
|
res = api_add_json_feeder_to_queue(data_json)
|
|
|
|
return Response(json.dumps(res[0]), mimetype='application/json'), res[1]
|
|
|
|
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
2024-02-29 14:56:45 +01:00
|
|
|
# # # # # # # # # # # # # # # OBJECTS # # # # # # # # # # # # # # # # # # # TODO LIST OBJ TYPES + SUBTYPES
|
2024-02-26 15:35:48 +01:00
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
2024-02-29 14:56:45 +01:00
|
|
|
@api_rest.route("api/v1/object", methods=['GET']) # TODO options
|
2024-09-05 16:40:24 +02:00
|
|
|
@token_required('user')
|
2024-02-29 14:56:45 +01:00
|
|
|
def v1_object():
|
|
|
|
obj_gid = request.args.get('gid')
|
|
|
|
if obj_gid:
|
|
|
|
r = ail_objects.api_get_object_global_id(obj_gid)
|
|
|
|
else:
|
|
|
|
obj_type = request.args.get('type')
|
|
|
|
obj_subtype = request.args.get('subtype')
|
|
|
|
obj_id = request.args.get('id')
|
|
|
|
r = ail_objects.api_get_object(obj_type, obj_subtype, obj_id)
|
|
|
|
return create_json_response(r[0], r[1])
|
|
|
|
|
|
|
|
|
|
|
|
@api_rest.route("api/v1/obj/gid/<path:object_global_id>", methods=['GET']) # TODO REMOVE ME ????
|
2024-09-05 16:40:24 +02:00
|
|
|
@token_required('user')
|
2024-02-29 14:56:45 +01:00
|
|
|
def v1_object_global_id(object_global_id):
|
|
|
|
r = ail_objects.api_get_object_global_id(object_global_id)
|
|
|
|
return create_json_response(r[0], r[1])
|
2024-02-26 15:35:48 +01:00
|
|
|
|
2024-02-29 14:56:45 +01:00
|
|
|
# @api_rest.route("api/v1/object/<object_type>/<object_subtype>/<path:object_id>", methods=['GET'])
|
|
|
|
@api_rest.route("api/v1/obj/<object_type>/<path:object_id>", methods=['GET']) # TODO REMOVE ME ????
|
2024-09-05 16:40:24 +02:00
|
|
|
@token_required('user')
|
2024-02-29 14:56:45 +01:00
|
|
|
def v1_object_type_id(object_type, object_id):
|
|
|
|
r = ail_objects.api_get_object_type_id(object_type, object_id)
|
|
|
|
return create_json_response(r[0], r[1])
|
2024-02-26 15:35:48 +01:00
|
|
|
|
2024-02-29 16:18:22 +01:00
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
# # # # # # # # # # # # # # # CHATS # # # # # # # # # # # # # # # # # # #
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
|
|
|
|
@api_rest.route("api/v1/chat/messages", methods=['GET'])
|
2024-09-05 16:40:24 +02:00
|
|
|
@token_required('user')
|
2024-02-29 16:18:22 +01:00
|
|
|
def objects_chat_messages():
|
|
|
|
obj_subtype = request.args.get('subtype')
|
|
|
|
obj_id = request.args.get('id')
|
|
|
|
r = chats_viewer.api_chat_messages(obj_subtype, obj_id)
|
|
|
|
return create_json_response(r[0], r[1])
|
|
|
|
|
|
|
|
@api_rest.route("api/v1/chat-subchannel/messages", methods=['GET'])
|
2024-09-05 16:40:24 +02:00
|
|
|
@token_required('user')
|
2024-02-29 16:18:22 +01:00
|
|
|
def objects_chat_subchannel_messages():
|
|
|
|
obj_subtype = request.args.get('subtype')
|
|
|
|
obj_id = request.args.get('id')
|
|
|
|
r = chats_viewer.api_subchannel_messages(obj_subtype, obj_id)
|
|
|
|
return create_json_response(r[0], r[1])
|
|
|
|
|
|
|
|
@api_rest.route("api/v1/chat-thread/messages", methods=['GET'])
|
2024-09-05 16:40:24 +02:00
|
|
|
@token_required('user')
|
2024-02-29 16:18:22 +01:00
|
|
|
def objects_chat_thread_messages():
|
|
|
|
obj_subtype = request.args.get('subtype')
|
|
|
|
obj_id = request.args.get('id')
|
|
|
|
r = chats_viewer.api_thread_messages(obj_subtype, obj_id)
|
|
|
|
return create_json_response(r[0], r[1])
|
|
|
|
|
2024-02-26 15:35:48 +01:00
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
2024-10-03 14:59:12 +02:00
|
|
|
# # # # # # # # # # # # # # # DOMAINS # # # # # # # # # # # # # # # # # #
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
|
2024-10-04 11:53:55 +02:00
|
|
|
@api_rest.route("api/v1/lookup/onion/<domain>", methods=['GET'])
|
2024-10-03 14:59:12 +02:00
|
|
|
@token_required('user')
|
2024-10-04 11:53:55 +02:00
|
|
|
def api_lookup_onion(domain):
|
|
|
|
return create_json_response(crawlers.api_get_onion_lookup(domain), 200)
|
2024-10-03 14:59:12 +02:00
|
|
|
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
2024-02-29 14:56:45 +01:00
|
|
|
# # # # # # # # # # # # # # # TITLES # # # # # # # # # # # # # # # # # # # TODO TO REVIEW
|
2024-02-26 15:35:48 +01:00
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
|
2024-02-29 16:18:22 +01:00
|
|
|
@api_rest.route("api/v1/titles/download", methods=['GET']) # TODO RENAME ->api/v1/titles/domains
|
2024-09-05 16:40:24 +02:00
|
|
|
@token_required('user')
|
2024-02-26 15:35:48 +01:00
|
|
|
def objects_titles_download():
|
|
|
|
return create_json_response(Titles.Titles().get_contents_ids(), 200)
|
|
|
|
|
|
|
|
|
|
|
|
# TODO
|
2024-02-29 16:18:22 +01:00
|
|
|
@api_rest.route("api/v1/titles/download/unsafe", methods=['GET']) # TODO RENAME ->api/v1/titles/domains/unsafe
|
2024-09-05 16:40:24 +02:00
|
|
|
@token_required('user')
|
2024-02-26 15:35:48 +01:00
|
|
|
def objects_titles_download_unsafe():
|
|
|
|
all_titles = {}
|
|
|
|
unsafe_tags = Tag.unsafe_tags
|
|
|
|
for tag in unsafe_tags:
|
|
|
|
domains = Tag.get_tag_objects(tag, 'domain')
|
|
|
|
for domain_id in domains:
|
|
|
|
domain = Domains.Domain(domain_id)
|
|
|
|
domain_titles = domain.get_correlation('title').get('title', [])
|
|
|
|
for dt in domain_titles:
|
|
|
|
title = Titles.Title(dt[1:])
|
|
|
|
title_content = title.get_content()
|
|
|
|
if title_content and title_content != 'None':
|
|
|
|
if title_content not in all_titles:
|
|
|
|
all_titles[title_content] = []
|
|
|
|
all_titles[title_content].append(domain.get_id())
|
|
|
|
return Response(json.dumps(all_titles), mimetype='application/json'), 200
|
|
|
|
|
2024-02-29 14:56:45 +01:00
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
# # # # # # # # # # # # # # # INVESTIGATIONS # # # # # # # # # # # # # # #
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
2024-02-29 16:18:22 +01:00
|
|
|
|
2024-02-29 14:56:45 +01:00
|
|
|
@api_rest.route("api/v1/investigation/<investigation_uuid>", methods=['GET']) # TODO options
|
2024-09-05 16:40:24 +02:00
|
|
|
@token_required('user')
|
2024-02-29 14:56:45 +01:00
|
|
|
def v1_investigation(investigation_uuid):
|
2024-08-27 15:48:11 +02:00
|
|
|
user_token = get_auth_from_header()
|
2024-09-05 14:41:13 +02:00
|
|
|
user_org, user_id, user_role = ail_api.get_basic_user_meta(user_token)
|
|
|
|
r = Investigations.api_get_investigation(user_org, user_id, user_role, investigation_uuid)
|
2024-02-29 14:56:45 +01:00
|
|
|
return create_json_response(r[0], r[1])
|
2024-02-26 15:35:48 +01:00
|
|
|
|
2024-02-29 14:56:45 +01:00
|
|
|
# TODO CATCH REDIRECT
|