mirror of https://github.com/CIRCL/AIL-framework
				
				
				
			
		
			
				
	
	
		
			269 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			269 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
#!/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
 | 
						|
from lib import ail_logger
 | 
						|
from lib import crawlers
 | 
						|
from lib import chats_viewer
 | 
						|
 | 
						|
from lib import Investigations
 | 
						|
from lib import Tag
 | 
						|
 | 
						|
from lib.objects import ail_objects
 | 
						|
from lib.objects import Domains
 | 
						|
from lib.objects import Titles
 | 
						|
 | 
						|
from importer.FeederImporter import api_add_json_feeder_to_queue
 | 
						|
 | 
						|
 | 
						|
# LOGS
 | 
						|
# access_logger = ail_logger.get_access_config()
 | 
						|
 | 
						|
 | 
						|
# ============ 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()
 | 
						|
            ip_source = request.access_route[0]
 | 
						|
            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
 | 
						|
                    # print(funct.__name__)
 | 
						|
                    return funct(*args, **kwargs)
 | 
						|
            else:
 | 
						|
                return create_json_response({'status': 'error', 'reason': 'Internal'}, 400)
 | 
						|
 | 
						|
        return api_token
 | 
						|
    return actual_decorator
 | 
						|
 | 
						|
 | 
						|
# ============ FUNCTIONS ============
 | 
						|
 | 
						|
def create_json_response(data, status_code):
 | 
						|
    return Response(json.dumps(data) + "\n", mimetype='application/json'), status_code
 | 
						|
 | 
						|
# ============= ROUTES ==============
 | 
						|
 | 
						|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 | 
						|
# # # # # # # # # # # # # # #        CORE       # # # # # # # # # # # # # # # # # #
 | 
						|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 | 
						|
 | 
						|
@api_rest.route("api/v1/ping", methods=['GET'])
 | 
						|
@token_required('user')
 | 
						|
def v1_ping():
 | 
						|
    return create_json_response({'status': 'pong'}, 200)
 | 
						|
 | 
						|
@api_rest.route("api/v1/uuid", methods=['GET'])
 | 
						|
@token_required('user')
 | 
						|
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'])
 | 
						|
@token_required('user')
 | 
						|
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'])
 | 
						|
@token_required('user')
 | 
						|
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
 | 
						|
@token_required('user')
 | 
						|
def add_crawler_task():
 | 
						|
    data = request.get_json()
 | 
						|
    user_token = get_auth_from_header()
 | 
						|
    user_org, user_id, _ = get_basic_user_meta(token)
 | 
						|
    res = crawlers.api_add_crawler_task(data, user_org, user_id=user_id)
 | 
						|
    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
 | 
						|
@token_required('user')
 | 
						|
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]
 | 
						|
 | 
						|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 | 
						|
# # # # # # # # # # # # # # #      OBJECTS      # # # # # # # # # # # # # # # # # # # TODO LIST OBJ TYPES + SUBTYPES
 | 
						|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 | 
						|
@api_rest.route("api/v1/object", methods=['GET'])  # TODO options
 | 
						|
@token_required('user')
 | 
						|
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 ????
 | 
						|
@token_required('user')
 | 
						|
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])
 | 
						|
 | 
						|
# @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 ????
 | 
						|
@token_required('user')
 | 
						|
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])
 | 
						|
 | 
						|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 | 
						|
# # # # # # # # # # # # # # #      CHATS      # # # # # # # # # # # # # # # # # # #
 | 
						|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 | 
						|
 | 
						|
@api_rest.route("api/v1/chat/messages", methods=['GET'])
 | 
						|
@token_required('user')
 | 
						|
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'])
 | 
						|
@token_required('user')
 | 
						|
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'])
 | 
						|
@token_required('user')
 | 
						|
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])
 | 
						|
 | 
						|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 | 
						|
# # # # # # # # # # # # # # #      DOMAINS      # # # # # # # # # # # # # # # # # #
 | 
						|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 | 
						|
 | 
						|
@api_rest.route("api/v1/lookup/onion/<domain>", methods=['GET'])
 | 
						|
@token_required('user')
 | 
						|
def api_lookup_onion(domain):
 | 
						|
    return create_json_response(crawlers.api_get_onion_lookup(domain), 200)
 | 
						|
 | 
						|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 | 
						|
# # # # # # # # # # # # # # #      TITLES       # # # # # # # # # # # # # # # # # # # TODO TO REVIEW
 | 
						|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 | 
						|
 | 
						|
@api_rest.route("api/v1/titles/download", methods=['GET'])  # TODO RENAME ->api/v1/titles/domains
 | 
						|
@token_required('user')
 | 
						|
def objects_titles_download():
 | 
						|
    return create_json_response(Titles.Titles().get_contents_ids(), 200)
 | 
						|
 | 
						|
 | 
						|
# TODO
 | 
						|
@api_rest.route("api/v1/titles/download/unsafe", methods=['GET'])  # TODO RENAME ->api/v1/titles/domains/unsafe
 | 
						|
@token_required('user')
 | 
						|
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
 | 
						|
 | 
						|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 | 
						|
# # # # # # # # # # # # # # #      INVESTIGATIONS     # # # # # # # # # # # # # # #
 | 
						|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 | 
						|
 | 
						|
@api_rest.route("api/v1/investigation/<investigation_uuid>", methods=['GET'])  # TODO options
 | 
						|
@token_required('user')
 | 
						|
def v1_investigation(investigation_uuid):
 | 
						|
    user_token = get_auth_from_header()
 | 
						|
    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)
 | 
						|
    return create_json_response(r[0], r[1])
 | 
						|
 | 
						|
# TODO CATCH REDIRECT
 |