chg: [api] refactor blueprint

This commit is contained in:
terrtia 2024-02-26 15:35:48 +01:00
parent 40b1378b30
commit ad63651838
No known key found for this signature in database
GPG key ID: 1E1B1F50D84613D0
7 changed files with 386 additions and 283 deletions

83
bin/lib/ail_api.py Executable file
View file

@ -0,0 +1,83 @@
#!/usr/bin/env python3
# -*-coding:UTF-8 -*
import os
import re
import sys
sys.path.append(os.environ['AIL_BIN'])
##################################
# Import Project packages
##################################
from lib.ConfigLoader import ConfigLoader
from lib import Users
config_loader = ConfigLoader()
r_cache = config_loader.get_redis_conn("Redis_Cache")
config_loader = None
def check_token_format(token, search=re.compile(r'[^a-zA-Z0-9_-]').search): ####################################################
return not bool(search(token))
def is_valid_token(token):
return Users.exists_token(token)
def get_user_from_token(token):
return Users.get_token_user(token)
def is_user_in_role(role, token): # verify_user_role
# User without API
if role == 'user_no_api':
return False
user_id = get_user_from_token(token)
if user_id:
return Users.is_in_role(user_id, role)
else:
return False
#### Brute Force Protection ####
def get_failed_login(ip_address):
return r_cache.get(f'failed_login_ip_api:{ip_address}')
def incr_failed_login(ip_address):
r_cache.incr(f'failed_login_ip_api:{ip_address}')
r_cache.expire(f'failed_login_ip_api:{ip_address}', 300)
def get_brute_force_ttl(ip_address):
return r_cache.ttl(f'failed_login_ip_api:{ip_address}')
def is_brute_force_protected(ip_address):
failed_login = get_failed_login(ip_address)
if failed_login:
failed_login = int(failed_login)
if failed_login >= 5:
return True
else:
return False
return False
#### --Brute Force Protection-- ####
def authenticate_user(token, ip_address):
if is_brute_force_protected(ip_address):
ip_ttl = get_brute_force_ttl(ip_address)
return {'status': 'error', 'reason': f'Max Connection Attempts reached, Please wait {ip_ttl}s'}, 401
try:
if len(token) != 55:
return {'status': 'error', 'reason': 'Invalid Token Length, required==55'}, 400
if not check_token_format(token):
return {'status': 'error', 'reason': 'Malformed Authentication String'}, 400
if is_valid_token(token):
return True, 200
# Failed Login
else:
incr_failed_login(ip_address)
return {'status': 'error', 'reason': 'Authentication failed'}, 401
except Exception as e:
print(e) # TODO Logs
return {'status': 'error', 'reason': 'Malformed Authentication String'}, 400

View file

@ -3,7 +3,7 @@
import os import os
import sys import sys
from uuid import uuid4 import uuid
sys.path.append(os.environ['AIL_BIN']) sys.path.append(os.environ['AIL_BIN'])
################################## ##################################
@ -31,8 +31,16 @@ def _set_ail_uuid():
r_serv_db.set('ail:uuid', ail_uuid) r_serv_db.set('ail:uuid', ail_uuid)
return ail_uuid return ail_uuid
def is_valid_uuid_v4(header_uuid):
try:
header_uuid = header_uuid.replace('-', '')
uuid_test = uuid.UUID(hex=header_uuid, version=4)
return uuid_test.hex == header_uuid
except:
return False
def generate_uuid(): def generate_uuid():
return str(uuid4()) return str(uuid.uuid4())
#### AIL OBJECTS #### #### AIL OBJECTS ####

View file

@ -19,3 +19,6 @@ class ModuleQueueError(AILError):
class MISPConnectionError(AILError): class MISPConnectionError(AILError):
pass pass
class AILObjectUnknown(AILError):
pass

View file

@ -7,6 +7,10 @@ sys.path.append(os.environ['AIL_BIN'])
################################## ##################################
# Import Project packages # Import Project packages
################################## ##################################
from lib.exceptions import AILObjectUnknown
from lib.ConfigLoader import ConfigLoader from lib.ConfigLoader import ConfigLoader
from lib.ail_core import get_all_objects, get_object_all_subtypes from lib.ail_core import get_all_objects, get_object_all_subtypes
from lib import correlations_engine from lib import correlations_engine
@ -23,7 +27,7 @@ from lib.objects.Cves import Cve
from lib.objects.Decodeds import Decoded, get_all_decodeds_objects, get_nb_decodeds_objects from lib.objects.Decodeds import Decoded, get_all_decodeds_objects, get_nb_decodeds_objects
from lib.objects.Domains import Domain from lib.objects.Domains import Domain
from lib.objects import Etags from lib.objects import Etags
from lib.objects.Favicons import Favicon from lib.objects import Favicons
from lib.objects import FilesNames from lib.objects import FilesNames
from lib.objects import HHHashs from lib.objects import HHHashs
from lib.objects.Items import Item, get_all_items_objects, get_nb_items_objects from lib.objects.Items import Item, get_all_items_objects, get_nb_items_objects
@ -55,20 +59,16 @@ def sanitize_objs_types(objs):
l_types = get_all_objects() l_types = get_all_objects()
return l_types return l_types
#### OBJECT ####
def get_object(obj_type, subtype, obj_id): def get_object(obj_type, subtype, obj_id):
if not subtype:
if obj_type == 'item': if obj_type == 'item':
return Item(obj_id) return Item(obj_id)
elif obj_type == 'domain': elif obj_type == 'domain':
return Domain(obj_id) return Domain(obj_id)
elif obj_type == 'decoded': elif obj_type == 'decoded':
return Decoded(obj_id) return Decoded(obj_id)
elif obj_type == 'chat':
return Chats.Chat(obj_id, subtype)
elif obj_type == 'chat-subchannel':
return ChatSubChannels.ChatSubChannel(obj_id, subtype)
elif obj_type == 'chat-thread':
return ChatThreads.ChatThread(obj_id, subtype)
elif obj_type == 'cookie-name': elif obj_type == 'cookie-name':
return CookiesNames.CookieName(obj_id) return CookiesNames.CookieName(obj_id)
elif obj_type == 'cve': elif obj_type == 'cve':
@ -76,7 +76,7 @@ def get_object(obj_type, subtype, obj_id):
elif obj_type == 'etag': elif obj_type == 'etag':
return Etags.Etag(obj_id) return Etags.Etag(obj_id)
elif obj_type == 'favicon': elif obj_type == 'favicon':
return Favicon(obj_id) return Favicons.Favicon(obj_id)
elif obj_type == 'file-name': elif obj_type == 'file-name':
return FilesNames.FileName(obj_id) return FilesNames.FileName(obj_id)
elif obj_type == 'hhhash': elif obj_type == 'hhhash':
@ -87,20 +87,42 @@ def get_object(obj_type, subtype, obj_id):
return Message(obj_id) return Message(obj_id)
elif obj_type == 'screenshot': elif obj_type == 'screenshot':
return Screenshot(obj_id) return Screenshot(obj_id)
elif obj_type == 'title':
return Titles.Title(obj_id)
else:
raise AILObjectUnknown(f'Unknown AIL object: {obj_type} {subtype} {obj_id}')
# SUBTYPES
else:
if obj_type == 'chat':
return Chats.Chat(obj_id, subtype)
elif obj_type == 'chat-subchannel':
return ChatSubChannels.ChatSubChannel(obj_id, subtype)
elif obj_type == 'chat-thread':
return ChatThreads.ChatThread(obj_id, subtype)
elif obj_type == 'cryptocurrency': elif obj_type == 'cryptocurrency':
return CryptoCurrencies.CryptoCurrency(obj_id, subtype) return CryptoCurrencies.CryptoCurrency(obj_id, subtype)
elif obj_type == 'pgp': elif obj_type == 'pgp':
return Pgps.Pgp(obj_id, subtype) return Pgps.Pgp(obj_id, subtype)
elif obj_type == 'title':
return Titles.Title(obj_id)
elif obj_type == 'user-account': elif obj_type == 'user-account':
return UserAccount(obj_id, subtype) return UserAccount(obj_id, subtype)
elif obj_type == 'username': elif obj_type == 'username':
return Usernames.Username(obj_id, subtype) return Usernames.Username(obj_id, subtype)
else: else:
raise Exception(f'Unknown AIL object: {obj_type} {subtype} {obj_id}') raise AILObjectUnknown(f'Unknown AIL object: {obj_type} {subtype} {obj_id}')
def get_objects(objects): def exists_obj(obj_type, subtype, obj_id):
obj = get_object(obj_type, subtype, obj_id)
if obj:
return obj.exists()
else:
return False
#########################################################################################
#########################################################################################
#########################################################################################
def get_objects(objects): # TODO RENAME ME
objs = set() objs = set()
for obj in objects: for obj in objects:
if isinstance(obj, dict): if isinstance(obj, dict):
@ -119,14 +141,6 @@ def get_objects(objects):
return ail_objects return ail_objects
def exists_obj(obj_type, subtype, obj_id):
obj = get_object(obj_type, subtype, obj_id)
if obj:
return obj.exists()
else:
return False
def get_obj_global_id(obj_type, subtype, obj_id): def get_obj_global_id(obj_type, subtype, obj_id):
obj = get_object(obj_type, subtype, obj_id) obj = get_object(obj_type, subtype, obj_id)
return obj.get_global_id() return obj.get_global_id()

View file

@ -53,6 +53,8 @@ from blueprints.objects_hhhash import objects_hhhash
from blueprints.chats_explorer import chats_explorer from blueprints.chats_explorer import chats_explorer
from blueprints.objects_image import objects_image from blueprints.objects_image import objects_image
from blueprints.objects_favicon import objects_favicon from blueprints.objects_favicon import objects_favicon
from blueprints.api_rest import api_rest
Flask_dir = os.environ['AIL_FLASK'] Flask_dir = os.environ['AIL_FLASK']
@ -113,6 +115,7 @@ app.register_blueprint(objects_hhhash, url_prefix=baseUrl)
app.register_blueprint(chats_explorer, url_prefix=baseUrl) app.register_blueprint(chats_explorer, url_prefix=baseUrl)
app.register_blueprint(objects_image, url_prefix=baseUrl) app.register_blueprint(objects_image, url_prefix=baseUrl)
app.register_blueprint(objects_favicon, url_prefix=baseUrl) app.register_blueprint(objects_favicon, url_prefix=baseUrl)
app.register_blueprint(api_rest, url_prefix=baseUrl)
# ========= =========# # ========= =========#
@ -125,8 +128,6 @@ login_manager = LoginManager()
login_manager.login_view = 'root.login' login_manager.login_view = 'root.login'
login_manager.init_app(app) login_manager.init_app(app)
print()
# ========= LOGIN MANAGER ======== # ========= LOGIN MANAGER ========
@login_manager.user_loader @login_manager.user_loader
@ -257,6 +258,10 @@ default_taxonomies = ["infoleak", "gdpr", "fpf", "dark-web"]
for taxonomy in default_taxonomies: for taxonomy in default_taxonomies:
Tag.enable_taxonomy_tags(taxonomy) Tag.enable_taxonomy_tags(taxonomy)
# rrrr = [str(p) for p in app.url_map.iter_rules()]
# for p in rrrr:
# print(p)
# ============ MAIN ============ # ============ MAIN ============
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -0,0 +1,187 @@
#!/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 crawlers
from lib import Tag
from lib.objects import ail_objects
from importer.FeederImporter import api_add_json_feeder_to_queue
from lib.objects import Domains
from lib.objects import Titles
# ============ 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.remote_addr
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
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): # TODO REMOVE INDENT ????????????????????
return Response(json.dumps(data, indent=2, sort_keys=True), mimetype='application/json'), status_code
# ============= ROUTES ==============
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # CORE # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
@api_rest.route("api/v1/ping", methods=['GET'])
@token_required('read_only')
def v1_ping():
return create_json_response({'status': 'pong'}, 200)
@api_rest.route("api/v1/uuid", methods=['GET'])
@token_required('read_only')
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('read_only')
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('read_only')
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('analyst')
def add_crawler_task():
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_task(data, 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('analyst')
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 # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # TITLES # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
@api_rest.route("api/v1/titles/download", methods=['GET'])
@token_required('analyst')
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 REFACTOR ME
@token_required('analyst')
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

View file

@ -5,153 +5,39 @@
Flask functions and routes for the rest api Flask functions and routes for the rest api
""" """
import os # import os
import re # import re
import sys # import sys
import uuid # import uuid
import json # import json
sys.path.append(os.environ['AIL_BIN']) # sys.path.append(os.environ['AIL_BIN'])
################################## # ##################################
# Import Project packages # # Import Project packages
################################## # ##################################
from lib.ConfigLoader import ConfigLoader # from lib.ConfigLoader import ConfigLoader
from lib import crawlers # from lib import Users
from lib import Users # from lib.objects import Items
from lib.objects import Items # from lib import Tag
from lib.objects import Titles #
from lib.objects import Domains # from packages import Import_helper
from lib import Tag #
from lib import Tracker # from importer.FeederImporter import api_add_json_feeder_to_queue
#
from packages import Import_helper #
# from flask import jsonify, request, Blueprint, redirect, url_for, Response
from importer.FeederImporter import api_add_json_feeder_to_queue #
# from functools import wraps
from flask import jsonify, request, Blueprint, redirect, url_for, Response
from functools import wraps
# ============ VARIABLES ============ # ============ VARIABLES ============
config_loader = ConfigLoader()
baseUrl = config_loader.get_config_str("Flask", "baseurl")
baseUrl = baseUrl.replace('/', '')
if baseUrl != '':
baseUrl = '/' + baseUrl
r_cache = config_loader.get_redis_conn("Redis_Cache")
config_loader = None
import Flask_config
app = Flask_config.app
restApi = Blueprint('restApi', __name__, template_folder='templates')
# ============ AUTH FUNCTIONS ============
def check_token_format(token, search=re.compile(r'[^a-zA-Z0-9_-]').search):
return not bool(search(token))
def verify_token(token):
if len(token) != 55:
return False
if not check_token_format(token):
return False
return Users.exists_token(token)
def verify_user_role(role, token):
# User without API
if role == 'user_no_api':
return False
user_id = Users.get_token_user(token)
if user_id:
return Users.is_in_role(user_id, role)
else:
return False
# ============ DECORATOR ============ # ============ DECORATOR ============
def token_required(user_role):
def actual_decorator(funct):
@wraps(funct)
def api_token(*args, **kwargs):
data = auth_errors(user_role)
if data:
return Response(json.dumps(data[0], indent=2, sort_keys=True), mimetype='application/json'), data[1]
else:
return funct(*args, **kwargs)
return api_token
return actual_decorator
def get_auth_from_header():
token = request.headers.get('Authorization').replace(' ', '') # remove space
return token
def auth_errors(user_role):
# Check auth
if not request.headers.get('Authorization'):
return {'status': 'error', 'reason': 'Authentication needed'}, 401
token = get_auth_from_header()
data = None
# verify token format
# brute force protection
current_ip = request.remote_addr
login_failed_ip = r_cache.get(f'failed_login_ip_api:{current_ip}')
# brute force by ip
if login_failed_ip:
login_failed_ip = int(login_failed_ip)
if login_failed_ip >= 5:
return {'status': 'error',
'reason': 'Max Connection Attempts reached, Please wait {}s'.format(r_cache.ttl('failed_login_ip_api:{}'.format(current_ip)))
}, 401
try:
authenticated = False
if verify_token(token): # TODO Improve Returned error
authenticated = True
# check user role
if not verify_user_role(user_role, token):
data = ({'status': 'error', 'reason': 'Access Forbidden'}, 403)
if not authenticated:
r_cache.incr(f'failed_login_ip_api:{current_ip}')
r_cache.expire(f'failed_login_ip_api:{current_ip}', 300)
data = ({'status': 'error', 'reason': 'Authentication failed'}, 401)
except Exception as e:
print(e)
data = ({'status': 'error', 'reason': 'Malformed Authentication String'}, 400)
if data:
return data
else:
return None
# ============ API CORE =============
def create_json_response(data_dict, response_code):
return Response(json.dumps(data_dict, indent=2, sort_keys=True), mimetype='application/json'), int(response_code)
def get_mandatory_fields(json_data, required_fields):
for field in required_fields:
if field not in json_data:
return {'status': 'error', 'reason': 'mandatory field: {} not provided'.format(field)}, 400
return None
# ============ FUNCTIONS ============
def is_valid_uuid_v4(header_uuid):
try:
header_uuid = header_uuid.replace('-', '')
uuid_test = uuid.UUID(hex=header_uuid, version=4)
return uuid_test.hex == header_uuid
except:
return False
# ============= ROUTES ============== # ============= ROUTES ==============
@ -160,6 +46,8 @@ def is_valid_uuid_v4(header_uuid):
# def api(): # def api():
# return 'api doc' # return 'api doc'
'''
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# POST # POST
# #
@ -303,11 +191,12 @@ def get_item_content_encoded_text():
def get_item_sources(): def get_item_sources():
res = Item.api_get_items_sources() res = Item.api_get_items_sources()
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1] return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
'''
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # TAGS # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # TAGS # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
'''
@restApi.route("api/v1/get/tag/metadata", methods=['POST']) @restApi.route("api/v1/get/tag/metadata", methods=['POST'])
@token_required('read_only') @token_required('read_only')
def get_tag_metadata(): def get_tag_metadata():
@ -323,6 +212,7 @@ def get_tag_metadata():
def get_all_tags(): def get_all_tags():
res = {'tags': Tag.get_all_tags()} res = {'tags': Tag.get_all_tags()}
return Response(json.dumps(res, indent=2, sort_keys=True), mimetype='application/json'), 200 return Response(json.dumps(res, indent=2, sort_keys=True), mimetype='application/json'), 200
'''
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # TODO # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # TODO
# # # # # # # # # # # # # # TRACKER # # # # # # # # # # # # # # # # # TODO # # # # # # # # # # # # # # TRACKER # # # # # # # # # # # # # # # # # TODO
@ -506,42 +396,11 @@ def get_item_cryptocurrency_bitcoin():
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1] return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
''' '''
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # CRAWLER # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # TODO: ADD RESULT JSON Response
# @restApi.route("api/v1/crawler/task/add", methods=['POST'])
@restApi.route("api/v1/add/crawler/task", methods=['POST'])
@token_required('analyst')
def add_crawler_task():
data = request.get_json()
user_token = get_auth_from_header()
user_id = Users.get_token_user(user_token)
res = crawlers.api_add_crawler_task(data, 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)
@restApi.route("api/v1/add/crawler/capture", methods=['POST'])
@token_required('analyst')
def add_crawler_capture():
data = request.get_json()
user_token = get_auth_from_header()
user_id = Users.get_token_user(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)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # DOMAIN # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # DOMAIN # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
'''
@restApi.route("api/v1/get/domain/status/minimal", methods=['POST']) @restApi.route("api/v1/get/domain/status/minimal", methods=['POST'])
@token_required('analyst') @token_required('analyst')
def get_domain_status_minimal(): def get_domain_status_minimal():
@ -558,6 +417,7 @@ def get_domain_status_minimal():
# res = Domain.api_get_domain_up_range(domain) # res = Domain.api_get_domain_up_range(domain)
res[0]['domain'] = domain res[0]['domain'] = domain
return create_json_response(res[0], res[1]) return create_json_response(res[0], res[1])
'''
# @restApi.route("api/v1/get/crawled/domain/list", methods=['POST']) # @restApi.route("api/v1/get/crawled/domain/list", methods=['POST'])
# @token_required('analyst') # @token_required('analyst')
@ -601,6 +461,7 @@ def get_domain_status_minimal():
# response: {"uuid": "uuid"} # response: {"uuid": "uuid"}
# #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
'''
@restApi.route("api/v1/import/item", methods=['POST']) @restApi.route("api/v1/import/item", methods=['POST'])
@token_required('analyst') @token_required('analyst')
def import_item(): def import_item():
@ -664,62 +525,4 @@ def import_item_uuid():
return Response(json.dumps(data[0]), mimetype='application/json'), data[1] return Response(json.dumps(data[0]), mimetype='application/json'), data[1]
return Response(json.dumps({'status': 'error', 'reason': 'Invalid response'}), mimetype='application/json'), 400 return Response(json.dumps({'status': 'error', 'reason': 'Invalid response'}), mimetype='application/json'), 400
'''
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
@restApi.route("api/v1/import/json/item", methods=['POST'])
@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]
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # CORE # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
@restApi.route("api/v1/ping", methods=['GET'])
@token_required('read_only')
def v1_ping():
return Response(json.dumps({'status': 'pong'}), mimetype='application/json'), 200
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # OTHERS # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
@restApi.route("api/v1/titles/download", methods=['GET'])
@token_required('analyst')
def objects_titles_download():
return Response(json.dumps(Titles.Titles().get_contents_ids()), mimetype='application/json'), 200
@restApi.route("api/v1/titles/download/unsafe", methods=['GET'])
@token_required('analyst')
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 titl in domain_titles:
title = Titles.Title(titl[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
# ========= REGISTRATION =========
app.register_blueprint(restApi, url_prefix=baseUrl)