chg: [external_tools] api_key, remove redirect

pull/689/head
David Cruciani 2024-08-22 12:06:37 +02:00
parent feeeaddeb1
commit 230eaf8437
No known key found for this signature in database
GPG Key ID: 8690CDE1E3994B9B
11 changed files with 135 additions and 13 deletions

View File

@ -96,6 +96,7 @@ class ExternalTools(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(64), index=True)
url = db.Column(db.String)
api_key = db.Column(db.String(60), index=True)
is_active = db.Column(db.Boolean)
def to_json(self):
@ -103,6 +104,7 @@ class ExternalTools(db.Model):
"id": self.id,
"url": self.url,
"name": self.name,
"api_key": self.api_key,
"is_active": self.is_active
}

View File

@ -61,10 +61,12 @@ def change_config():
if "tool_id" in request.json["result_dict"] and request.json["result_dict"]["tool_id"]:
if "tool_name" in request.json["result_dict"] and request.json["result_dict"]["tool_name"]:
if "tool_url" in request.json["result_dict"] and request.json["result_dict"]["tool_url"]:
res = ToolModel.change_config_core(request.json["result_dict"])
if res:
return {'message': 'Config changed', 'toast_class': "success-subtle"}, 200
return {'message': 'Something went wrong', 'toast_class': "danger-subtle"}, 400
if "tool_api_key" in request.json["result_dict"] and request.json["result_dict"]["tool_api_key"]:
res = ToolModel.change_config_core(request.json["result_dict"])
if res:
return {'message': 'Config changed', 'toast_class': "success-subtle"}, 200
return {'message': 'Something went wrong', 'toast_class': "danger-subtle"}, 400
return {'message': 'Need to pass "tool_api_key"', 'toast_class': "warning-subtle"}, 400
return {'message': 'Need to pass "tool_url"', 'toast_class': "warning-subtle"}, 400
return {'message': 'Need to pass "tool_name"', 'toast_class': "warning-subtle"}, 400
return {'message': 'Need to pass "tool_id"', 'toast_class': "warning-subtle"}, 400

View File

@ -25,6 +25,7 @@ def change_config_core(request_json):
if tool:
tool.name = request_json["tool_name"]
tool.url = request_json["tool_url"]
tool.api_key = request_json["tool_api_key"]
db.session.commit()
return True
return False
@ -34,6 +35,7 @@ def add_tool_core(form_dict):
tool = ExternalTools(
name=form_dict["name"],
url = form_dict["url"],
api_key = form_dict["api_key"],
is_active=True
)
db.session.add(tool)

View File

@ -9,4 +9,5 @@ from wtforms.validators import InputRequired, Length
class ExternalToolForm(FlaskForm):
name = StringField('Name', validators=[InputRequired(), Length(1, 64)])
url = StringField('Url', validators=[InputRequired()])
api_key = StringField('API key', validators=[InputRequired(), Length(1, 60)])
submit = SubmitField('Create')

View File

@ -2,9 +2,11 @@ import ast
import json
from flask import Blueprint, render_template, request, jsonify, session as sess
from flask_login import current_user
import requests
from . import session_class as SessionModel
from . import home_core as HomeModel
from .utils.utils import admin_user_active
from .external_tools import external_tools_core as ToolModel
home_blueprint = Blueprint(
'home',
@ -255,3 +257,19 @@ def change_status():
return {'message': 'Need to pass "module_id"', 'toast_class': "warning-subtle"}, 400
return {'message': 'Permission denied', 'toast_class': "danger-subtle"}, 403
@home_blueprint.route("/submit_external_tool", methods=["GET", "POST"])
def submit_external_tool():
"""Submit result to an external tool"""
sess["admin_user"] = admin_user_active()
flag = True
if sess.get("admin_user"):
if not current_user.is_authenticated:
flag = False
# if admin is active and user is logon or if admin is not active
if flag:
ext = ToolModel.get_tool(request.json["external_tool_id"])
if HomeModel.submit_external_tool(request.json["results"], ext):
return {'message': f'Send to {ext.name} successfully', 'toast_class': "success-subtle"}, 200
return {'message': 'Something went wrong', 'toast_class': "danger-subtle"}, 400
return {'message': 'Permission denied', 'toast_class': "danger-subtle"}, 403

View File

@ -1,4 +1,6 @@
import json
import requests
from .utils.utils import isUUID, query_get_module
from . import db
from .db_class.db import History, Module, Config, Module_Config, Session_db, History_Tree
@ -113,6 +115,13 @@ def change_status_core(module_id):
db.session.commit()
return True
def submit_external_tool(results, ext_tool):
headers = {'Content-Type': 'application/json', "X-API-KEY": ext_tool.api_key, "Origin": "misp-module"}
response = requests.post(ext_tool.url, json={"results":results}, headers=headers)
if response.status_code == 200:
return True
return False
##############

View File

@ -26,6 +26,15 @@
{%endif%}
</div>
</div>
<div class="row">
<div class="mb-3 w-50">
{{form.api_key.label(class_="col-form-label")}}:
{{form.api_key(class_="form-control")}}
{% if form.api_key.errors %}
<div style="color: red;">{{form.api_key.errors[0] | safe}}</div>
{%endif%}
</div>
</div>
{{form.submit(class='btn btn-primary')}}
</form>

View File

@ -53,6 +53,11 @@
<input type="text" class="form-control" :id="'form-url-'+current_config.tool_id" :value="current_config.tool_url">
<span style="color: brown" :id="'error-url-'+current_config.tool_id"></span>
</div>
<div class="mb-3">
<label :for="'form-api-key-'+current_config.tool_id" class="form-label">API key: </label>
<input type="text" class="form-control" :id="'form-api-key-'+current_config.tool_id" :value="current_config.tool_api_key">
<span style="color: brown" :id="'error-api-key-'+current_config.tool_id"></span>
</div>
<button class="btn btn-primary" @click="change_config()">Save</button>
<button class="btn btn-danger" @click="delete_tool()" style="margin-left: 5px;">Delete</button>
</div>
@ -86,6 +91,7 @@
current_config.value["tool_name"] = tool.name
current_config.value["tool_url"] = tool.url
current_config.value["tool_id"] = tool.id
current_config.value["tool_api_key"] = tool.api_key
}
function close_panel(){
@ -95,6 +101,7 @@
async function change_config(){
$("#error-name-"+current_config.value["tool_id"]).text("")
$("#error-url-"+current_config.value["tool_id"]).text("")
$("#error-url-"+current_config.value["tool_api_key"]).text("")
let result_dict = {}
result_dict["tool_id"] = current_config.value["tool_id"]
@ -108,6 +115,11 @@
$("#error-url-"+current_config.value["tool_id"]).text('Cannot be empty')
return
}
let loc_api_key = $("#form-api-key-"+current_config.value["tool_id"]).val()
if(!loc_api_key){
$("#error-api-key-"+current_config.value["tool_id"]).text('Cannot be empty')
return
}
// Update result_dict and current_config
result_dict["tool_name"] = loc_name
@ -115,12 +127,16 @@
result_dict["tool_url"] = loc_url
current_config.value["tool_url"] = loc_url
result_dict["tool_api_key"] = loc_api_key
current_config.value["tool_api_key"] = loc_api_key
// Update list of tools with new value for current tool
for(let i in tools_config.value){
if(tools_config.value[i].id == current_config.value["tool_id"] ){
tools_config.value[i].name = loc_name
tools_config.value[i].url = loc_url
tools_config.value[i].api_key = loc_api_key
}
}

View File

@ -292,7 +292,7 @@ Value: [[misp_attr]]
{% block script %}
<script type="module">
const { createApp, ref, onMounted, nextTick} = Vue
import {message_list} from '/static/js/toaster.js'
import {message_list, display_toast} from '/static/js/toaster.js'
import history_view from '/static/js/history/history_tree_query.js'
createApp({
delimiters: ['[[', ']]'],
@ -460,14 +460,13 @@ Value: [[misp_attr]]
to_return = modules_res.value
}
$('#insert_form').append(
$('<form>').attr(
{"action": external_tools.value[tool_selected].url, "name": "external_tools_form", "method": "post", "style": "display:none"}).append(
$("<input>").attr({"type": "text", "name": "results"}).val(JSON.stringify(to_return)
)
)
);
document.forms['external_tools_form'].submit();
const res = await fetch('/submit_external_tool',{
headers: { "X-CSRFToken": $("#csrf_token").val(), "Content-Type": "application/json" },
method: "POST",
body: JSON.stringify({"results": JSON.stringify(to_return), "external_tool_id": external_tools.value[tool_selected].id})
})
display_toast(res)
}
}

30
website/migrate.sh Executable file
View File

@ -0,0 +1,30 @@
#!/bin/bash
source env/bin/activate
export FLASKENV=development
function migrate {
flask db migrate
}
function upgrade {
flask db upgrade
}
function downgrade {
flask db downgrade
}
if [ "$1" ]; then
case $1 in
-m | --migrate ) migrate;
;;
-u | --upgrade ) upgrade;
;;
-d | --downgrade ) downgrade;
esac
shift
else
echo "need -m or -u or -d"
fi

View File

@ -0,0 +1,34 @@
"""empty message
Revision ID: 206e0e808b0c
Revises: 3a631d400f60
Create Date: 2024-08-21 09:36:37.801809
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '206e0e808b0c'
down_revision = '3a631d400f60'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('external_tools', schema=None) as batch_op:
batch_op.add_column(sa.Column('api_key', sa.String(length=60), nullable=True))
batch_op.create_index(batch_op.f('ix_external_tools_api_key'), ['api_key'], unique=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('external_tools', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_external_tools_api_key'))
batch_op.drop_column('api_key')
# ### end Alembic commands ###