mirror of
https://github.com/ail-project/ail-framework.git
synced 2024-11-26 15:57:16 +00:00
fix: [api] fix token length
This commit is contained in:
parent
e0899e6e0d
commit
3f8656a835
3 changed files with 105 additions and 134 deletions
|
@ -406,9 +406,43 @@ def _manual_set_items_date_first_last():
|
|||
if last != 0:
|
||||
update_obj_date(last, 'item')
|
||||
|
||||
#### API ####
|
||||
|
||||
def api_get_item(data):
|
||||
item_id = data.get('id', None)
|
||||
if not item_id:
|
||||
return {'status': 'error', 'reason': 'Mandatory parameter(s) not provided'}, 400
|
||||
item = Item(item_id)
|
||||
if not item.exists():
|
||||
return {'status': 'error', 'reason': 'Item not found'}, 404
|
||||
|
||||
options = set()
|
||||
if data.get('content'):
|
||||
options.add('content')
|
||||
if data.get('crawler'):
|
||||
options.add('crawler')
|
||||
if data.get('duplicates'):
|
||||
options.add('duplicates')
|
||||
if data.get('lines'):
|
||||
options.add('lines')
|
||||
if data.get('mimetype'):
|
||||
options.add('mimetype')
|
||||
if data.get('parent'):
|
||||
options.add('parent')
|
||||
if data.get('size'):
|
||||
options.add('size')
|
||||
|
||||
# TODO correlation
|
||||
|
||||
return item.get_meta(options=options), 200
|
||||
|
||||
|
||||
# -- API -- #
|
||||
|
||||
################################################################################
|
||||
################################################################################
|
||||
################################################################################
|
||||
|
||||
# TODO
|
||||
|
||||
def exist_item(item_id):
|
||||
|
|
|
@ -1239,64 +1239,36 @@ curl -k https://127.0.0.1:7000/api/v1/get/import/item --header "Authorization: i
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# FUTURE endpoints
|
||||
|
||||
### Text search by daterange
|
||||
##### ``api/search/textIndexer/item`` POST
|
||||
<details>
|
||||
<summary>Endpoints</summary>
|
||||
|
||||
### Get tagged items by daterange
|
||||
##### ``api/search/tag/item`` POST
|
||||
### Submit a domain to crawl TODO
|
||||
##### ``api/add/crawler/task`` POST
|
||||
|
||||
### Submit a domain to crawl
|
||||
##### ``api/add/crawler/domain`` POST
|
||||
### Create a term/set/regex/yara tracker
|
||||
##### ``api/add/tracker/`` POST
|
||||
|
||||
### Create a term/set/regex tracker
|
||||
##### ``api/add/termTracker/`` POST
|
||||
|
||||
### Get tracker items list
|
||||
##### ``api/get/termTracker/item`` POST
|
||||
### Get tracker
|
||||
##### ``api/get/tracker`` POST
|
||||
|
||||
-----
|
||||
|
||||
### Check if a tor/regular domain have been crawled
|
||||
##### ``api/get/crawler/domain/`` POST
|
||||
|
||||
### Check if a tor/regular domain have been crawled
|
||||
##### ``api/get/crawler/domain/metadata/ <domain><port>`` POST
|
||||
|
||||
### Get domain tags
|
||||
##### ``api/get/crawler/domain/tag/ <domain><port>`` POST
|
||||
##### ``api/get/domain/tags/<domain>`` POST
|
||||
|
||||
### Get domain history
|
||||
##### ``api/get/crawler/domain/history/ <domain><port>`` POST
|
||||
|
||||
### Get domain list of items
|
||||
##### ``api/get/crawler/domain/item/ <domain><port>`` POST
|
||||
##### ``api/get/domain/history/<domain>`` POST
|
||||
|
||||
-----
|
||||
|
||||
### Create auto-crawlers
|
||||
##### ``api/add/crawler/autoCrawler/`` POST
|
||||
|
||||
-----
|
||||
|
||||
### get item by mime type/ decoded type
|
||||
##### ``api/get/decoded`` POST
|
||||
|
||||
### Check if a decoded item exists (via sha1)
|
||||
##### ``api/get/decoded/exist/<sha1>`` POST
|
||||
|
||||
### Get decoded item metadata
|
||||
### Check if a decoded item exists (via sha1)
|
||||
##### ``api/get/decoded/metadata/<sha1>`` POST
|
||||
|
||||
### Get decoded item correlation (1 depth)
|
||||
##### ``api/get/decoded/metadata/<sha1>`` POST
|
||||
|
||||
-----
|
||||
|
||||
|
||||
|
@ -1304,18 +1276,20 @@ curl -k https://127.0.0.1:7000/api/v1/get/import/item --header "Authorization: i
|
|||
##### ``api/get/cryptocurrency`` POST
|
||||
|
||||
### Check if a cryptocurrency address (bitcoin, ..) exists
|
||||
##### ``api/get/cryptocurrency/exist/<bitcoin_address>`` POST
|
||||
##### ``api/get/cryptocurrency/<bitcoin_address>`` POST
|
||||
|
||||
### Get cryptocurrency address metadata
|
||||
##### ``api/get/cryptocurrency/metadata/<bitcoin_address>`` POST
|
||||
##### ``api/get/cryptocurrency/metadata/<coin_address>`` POST
|
||||
|
||||
-----
|
||||
|
||||
### Item correlation (1 depth)
|
||||
##### ``api/get/item/correlation/`` POST
|
||||
### Object correlation (1 depth)
|
||||
##### ``api/get/correlation/`` POST
|
||||
|
||||
### Create MISP event from item
|
||||
##### ``api/export/item/misp`` POST
|
||||
### Create MISP event from object
|
||||
##### ``api/export/misp`` POST
|
||||
|
||||
</details>
|
||||
|
||||
-----
|
||||
|
||||
### Create TheHive case from item
|
||||
##### ``api/export/item/thehive`` POST
|
||||
|
|
|
@ -10,38 +10,42 @@ import re
|
|||
import sys
|
||||
import uuid
|
||||
import json
|
||||
import datetime
|
||||
|
||||
sys.path.append(os.environ['AIL_BIN'])
|
||||
##################################
|
||||
# Import Project packages
|
||||
##################################
|
||||
from lib.ConfigLoader import ConfigLoader
|
||||
from lib import crawlers
|
||||
from lib import Users
|
||||
from lib.objects.Items import Item
|
||||
from lib.objects import Items
|
||||
from lib import Tag
|
||||
from lib import Tracker
|
||||
|
||||
from packages import Term
|
||||
from packages import Term # TODO REMOVE ME
|
||||
|
||||
from packages import Import_helper
|
||||
|
||||
from importer.FeederImporter import api_add_json_feeder_to_queue
|
||||
|
||||
|
||||
from flask import Flask, render_template, jsonify, request, Blueprint, redirect, url_for, Response, escape
|
||||
from flask_login import login_required
|
||||
from flask import jsonify, request, Blueprint, redirect, url_for, Response, escape
|
||||
|
||||
from functools import wraps
|
||||
|
||||
# ============ 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
|
||||
baseUrl = Flask_config.baseUrl
|
||||
r_cache = Flask_config.r_cache
|
||||
|
||||
|
||||
restApi = Blueprint('restApi', __name__, template_folder='templates')
|
||||
|
||||
|
@ -51,7 +55,7 @@ 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) != 41:
|
||||
if len(token) != 55:
|
||||
return False
|
||||
|
||||
if not check_token_format(token):
|
||||
|
@ -76,7 +80,7 @@ def token_required(user_role):
|
|||
def actual_decorator(funct):
|
||||
@wraps(funct)
|
||||
def api_token(*args, **kwargs):
|
||||
data = authErrors(user_role)
|
||||
data = auth_errors(user_role)
|
||||
if data:
|
||||
return Response(json.dumps(data[0], indent=2, sort_keys=True), mimetype='application/json'), data[1]
|
||||
else:
|
||||
|
@ -85,10 +89,10 @@ def token_required(user_role):
|
|||
return actual_decorator
|
||||
|
||||
def get_auth_from_header():
|
||||
token = request.headers.get('Authorization').replace(' ', '') # remove space
|
||||
token = request.headers.get('Authorization').replace(' ', '') # remove space
|
||||
return token
|
||||
|
||||
def authErrors(user_role):
|
||||
def auth_errors(user_role):
|
||||
# Check auth
|
||||
if not request.headers.get('Authorization'):
|
||||
return {'status': 'error', 'reason': 'Authentication needed'}, 401
|
||||
|
@ -98,16 +102,18 @@ def authErrors(user_role):
|
|||
|
||||
# brute force protection
|
||||
current_ip = request.remote_addr
|
||||
login_failed_ip = r_cache.get('failed_login_ip_api:{}'.format(current_ip))
|
||||
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
|
||||
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):
|
||||
if verify_token(token): # TODO Improve Returned error
|
||||
authenticated = True
|
||||
|
||||
# check user role
|
||||
|
@ -115,8 +121,8 @@ def authErrors(user_role):
|
|||
data = ({'status': 'error', 'reason': 'Access Forbidden'}, 403)
|
||||
|
||||
if not authenticated:
|
||||
r_cache.incr('failed_login_ip_api:{}'.format(current_ip))
|
||||
r_cache.expire('failed_login_ip_api:{}'.format(current_ip), 300)
|
||||
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)
|
||||
|
@ -141,7 +147,7 @@ def get_mandatory_fields(json_data, required_fields):
|
|||
|
||||
def is_valid_uuid_v4(header_uuid):
|
||||
try:
|
||||
header_uuid=header_uuid.replace('-', '')
|
||||
header_uuid = header_uuid.replace('-', '')
|
||||
uuid_test = uuid.UUID(hex=header_uuid, version=4)
|
||||
return uuid_test.hex == header_uuid
|
||||
except:
|
||||
|
@ -160,7 +166,6 @@ def is_valid_uuid_v4(header_uuid):
|
|||
# {
|
||||
# "id": item_id, mandatory
|
||||
# "content": true,
|
||||
# "tags": true,
|
||||
#
|
||||
#
|
||||
# }
|
||||
|
@ -175,17 +180,7 @@ def is_valid_uuid_v4(header_uuid):
|
|||
@token_required('read_only')
|
||||
def get_item_id():
|
||||
data = request.get_json()
|
||||
res = Item.get_item(data)
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
|
||||
@restApi.route("api/v1/get/item/default", methods=['POST'])
|
||||
@token_required('read_only')
|
||||
def get_item_id_basic():
|
||||
|
||||
data = request.get_json()
|
||||
item_id = data.get('id', None)
|
||||
req_data = {'id': item_id, 'date': True, 'content': True, 'tags': True}
|
||||
res = Item.get_item(req_data)
|
||||
res = Items.api_get_item(data)
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
|
@ -197,6 +192,7 @@ def get_item_id_basic():
|
|||
#
|
||||
# response: {
|
||||
# "id": "item_id",
|
||||
# "date": "date",
|
||||
# "tags": [],
|
||||
# }
|
||||
#
|
||||
|
@ -204,7 +200,6 @@ def get_item_id_basic():
|
|||
@restApi.route("api/v1/get/item/tag", methods=['POST'])
|
||||
@token_required('read_only')
|
||||
def get_item_tag():
|
||||
|
||||
data = request.get_json()
|
||||
item_id = data.get('id', None)
|
||||
req_data = {'id': item_id, 'date': False, 'tags': True}
|
||||
|
@ -309,17 +304,6 @@ def get_item_sources():
|
|||
res = Item.api_get_items_sources()
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
|
||||
|
||||
|
||||
# @restApi.route("api/v1/get/item/source/check", methods=['POST'])
|
||||
# @token_required('read_only')
|
||||
# def get_check_item_source():
|
||||
# data = request.get_json()
|
||||
# source = data.get('source', None)
|
||||
# req_data = {'source': source}
|
||||
# res = Item.check_item_source(req_data)
|
||||
# return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # # # # # # # TAGS # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
|
@ -390,6 +374,8 @@ def get_tracker_metadata_api():
|
|||
res = Tracker.get_tracker_metadata_api(req_data)
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=False), mimetype='application/json'), res[1]
|
||||
|
||||
'''
|
||||
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # # # # # CRYPTOCURRENCY # # # # # # # # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
|
@ -476,8 +462,6 @@ def get_pgp_name_item():
|
|||
res = 0
|
||||
return Response(json.dumps(res[0], indent=2, sort_keys=True), mimetype='application/json'), res[1]
|
||||
|
||||
'''
|
||||
|
||||
|
||||
|
||||
@restApi.route("api/v1/get/item/cryptocurrency/key", methods=['POST'])
|
||||
|
@ -511,25 +495,19 @@ def get_item_cryptocurrency_bitcoin():
|
|||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # # # # # # # CRAWLER # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
@restApi.route("api/v1/crawl/domain", methods=['POST'])
|
||||
# # TODO: ADD RESULT JSON Response
|
||||
@restApi.route("api/v1/add/crawler/task", methods=['POST'])
|
||||
@token_required('analyst')
|
||||
def crawl_domain():
|
||||
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])
|
||||
|
||||
url = data.get('url', None)
|
||||
screenshot = data.get('screenshot', None)
|
||||
har = data.get('har', None)
|
||||
depth_limit = data.get('depth_limit', None)
|
||||
max_pages = data.get('max_pages', None)
|
||||
auto_crawler = data.get('auto_crawler', None)
|
||||
crawler_delta = data.get('crawler_delta', None)
|
||||
crawler_type = data.get('url', None)
|
||||
cookiejar_uuid = data.get('url', None)
|
||||
user_agent = data.get('url', None)
|
||||
|
||||
res = crawlers.api_create_crawler_task(json_dict)
|
||||
res[0]['domain'] = domain
|
||||
return create_json_response(res[0], res[1])
|
||||
dict_res = {'url': data['url']}
|
||||
return create_json_response(dict_res, 200)
|
||||
|
||||
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
|
@ -574,27 +552,11 @@ def get_crawled_domain_list():
|
|||
dict_res['domain_type'] = domain_type
|
||||
return create_json_response(dict_res, res[1])
|
||||
|
||||
# # TODO: ADD RESULT JSON Response
|
||||
@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 = 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)
|
||||
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # # # # # # IMPORT # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
|
||||
|
||||
|
||||
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
#
|
||||
# POST JSON FORMAT
|
||||
|
@ -616,12 +578,12 @@ def import_item():
|
|||
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return Response(json.dumps({'status': 'error', 'reason': 'Malformed JSON'}, indent=2, sort_keys=True), mimetype='application/json'), 400
|
||||
return create_json_response({'status': 'error', 'reason': 'Malformed JSON'}, 400)
|
||||
|
||||
# unpack json
|
||||
text_to_import = data.get('text', None)
|
||||
if not text_to_import:
|
||||
return Response(json.dumps({'status': 'error', 'reason': 'No text supplied'}, indent=2, sort_keys=True), mimetype='application/json'), 400
|
||||
return create_json_response({'status': 'error', 'reason': 'No text supplied'}, 400)
|
||||
|
||||
tags = data.get('tags', [])
|
||||
if not type(tags) is list:
|
||||
|
@ -631,19 +593,19 @@ def import_item():
|
|||
galaxy = []
|
||||
|
||||
if not Tag.is_valid_tags_taxonomies_galaxy(tags, galaxy):
|
||||
return Response(json.dumps({'status': 'error', 'reason': 'Tags or Galaxy not enabled'}, indent=2, sort_keys=True), mimetype='application/json'), 400
|
||||
return create_json_response({'status': 'error', 'reason': 'Tags or Galaxy not enabled'}, 400)
|
||||
|
||||
default_tags = data.get('default_tags', True)
|
||||
if default_tags:
|
||||
tags.append('infoleak:submission="manual"')
|
||||
|
||||
if sys.getsizeof(text_to_import) > 900000:
|
||||
return Response(json.dumps({'status': 'error', 'reason': 'Size exceeds default'}, indent=2, sort_keys=True), mimetype='application/json'), 413
|
||||
return create_json_response({'status': 'error', 'reason': 'Size exceeds default'}, 413)
|
||||
|
||||
UUID = str(uuid.uuid4())
|
||||
Import_helper.create_import_queue(tags, galaxy, text_to_import, UUID)
|
||||
import_uuid = str(uuid.uuid4())
|
||||
Import_helper.create_import_queue(tags, galaxy, text_to_import, import_uuid)
|
||||
|
||||
return Response(json.dumps({'uuid': UUID}, indent=2, sort_keys=True), mimetype='application/json')
|
||||
return Response(json.dumps({'uuid': import_uuid}, indent=2, sort_keys=True), mimetype='application/json')
|
||||
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# GET
|
||||
|
@ -662,13 +624,13 @@ def import_item():
|
|||
@token_required('analyst')
|
||||
def import_item_uuid():
|
||||
data = request.get_json()
|
||||
UUID = data.get('uuid', None)
|
||||
import_uuid = data.get('uuid', None)
|
||||
|
||||
# Verify uuid
|
||||
if not is_valid_uuid_v4(UUID):
|
||||
if not is_valid_uuid_v4(import_uuid):
|
||||
return Response(json.dumps({'status': 'error', 'reason': 'Invalid uuid'}), mimetype='application/json'), 400
|
||||
|
||||
data = Import_helper.check_import_status(UUID)
|
||||
data = Import_helper.check_import_status(import_uuid)
|
||||
if data:
|
||||
return Response(json.dumps(data[0]), mimetype='application/json'), data[1]
|
||||
|
||||
|
@ -700,5 +662,6 @@ def import_json_item():
|
|||
def v1_ping():
|
||||
return Response(json.dumps({'status': 'pong'}), mimetype='application/json'), 200
|
||||
|
||||
|
||||
# ========= REGISTRATION =========
|
||||
app.register_blueprint(restApi, url_prefix=baseUrl)
|
||||
|
|
Loading…
Reference in a new issue