feat: frontend
This commit is contained in:
parent
875c0cc258
commit
4a7b66b481
7 changed files with 847 additions and 0 deletions
556
src/static/assets/index/scripts.js
Normal file
556
src/static/assets/index/scripts.js
Normal file
|
|
@ -0,0 +1,556 @@
|
|||
class Auth {
|
||||
constructor() {
|
||||
this.url = "http://localhost:8000"
|
||||
this.settings = {
|
||||
authority: "https://sso.sr2.uk/realms/sr2",
|
||||
client_id: "chris-dev",
|
||||
redirect_uri: this.url + "/signin-callback.html",
|
||||
response_type: "code",
|
||||
scope: "openid email profile",
|
||||
|
||||
response_mode: "fragment",
|
||||
}
|
||||
|
||||
this.user_manager = new oidc.UserManager(this.settings)
|
||||
this.get_user().then(() => {
|
||||
window.is_authenticated = Boolean(this.user);
|
||||
ui.update_auth_button()
|
||||
})
|
||||
}
|
||||
|
||||
async get_user() {
|
||||
this.user = await this.user_manager.getUser()
|
||||
}
|
||||
|
||||
async login() {
|
||||
try {
|
||||
this.user = await this.user_manager.signinPopup();
|
||||
window.is_authenticated = Boolean(auth.user);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async logout() {
|
||||
try {
|
||||
this.user = await this.user_manager.signoutPopup();
|
||||
window.is_authenticated = Boolean(auth.user);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class UI {
|
||||
constructor() {
|
||||
this.auth_button = document.getElementById("auth_button");
|
||||
|
||||
this.blocked_domains = document.getElementById("blocked_domains")
|
||||
this.timer_status = document.getElementById("timer_status")
|
||||
this.overall_status = document.getElementById("overall_status")
|
||||
this.last_update_complete_time = document.getElementById("last_update_complete_time")
|
||||
this.last_update_duration = document.getElementById("last_update_duration")
|
||||
|
||||
this.manual_update_btn = document.getElementById("manual_update_btn")
|
||||
this.start_timer_btn = document.getElementById("start_timer_btn")
|
||||
this.stop_timer_btn = document.getElementById("stop_timer_btn")
|
||||
|
||||
this.domain_search_btn = document.getElementById("domain_search_btn")
|
||||
|
||||
this.false_positive_load_btn = document.getElementById("false_positive_load_btn")
|
||||
this.event_container = document.getElementById("events_container")
|
||||
|
||||
this.create_listeners()
|
||||
}
|
||||
|
||||
create_listeners() {
|
||||
this.auth_button.addEventListener("click", this.onclick_auth_button)
|
||||
|
||||
this.manual_update_btn.addEventListener("click", this.onclick_manual_update_btn)
|
||||
this.start_timer_btn.addEventListener("click", this.onclick_start_timer_btn)
|
||||
this.stop_timer_btn.addEventListener("click", this.onclick_stop_timer_btn)
|
||||
|
||||
this.domain_search_btn.addEventListener("click", this.onclick_domain_search_btn)
|
||||
|
||||
this.false_positive_load_btn.addEventListener("click", this.onclick_false_positive_load_btn)
|
||||
this.event_container.addEventListener("click", this.onclick_event_button)
|
||||
|
||||
document.getElementById("modal_button").addEventListener("click", this.onclick_modal_button)
|
||||
}
|
||||
|
||||
open_modal(title, message) {
|
||||
document.getElementById("modal_title").innerText = title;
|
||||
document.getElementById("modal_message").innerText = message;
|
||||
|
||||
document.getElementById("modal_backdrop").style.display = "flex";
|
||||
}
|
||||
|
||||
set_loading(button_id, loading) {
|
||||
const button = document.getElementById(button_id);
|
||||
|
||||
button.disabled = loading;
|
||||
|
||||
if (loading) {
|
||||
button.dataset.original_text = button.innerText;
|
||||
button.innerText = "Loading...";
|
||||
} else {
|
||||
button.innerText = button.dataset.original_text;
|
||||
}
|
||||
}
|
||||
|
||||
sync_timer_buttons() {
|
||||
this.start_timer_btn.disabled = timer_running;
|
||||
this.stop_timer_btn.disabled = !timer_running;
|
||||
}
|
||||
|
||||
render_search_results(results) {
|
||||
const container = document.getElementById("domain_search_results");
|
||||
const result_count = Object.keys(results).length;
|
||||
if (!result_count) {
|
||||
container.innerHTML = `<div class="search-result">No results found.</div>`;
|
||||
return;
|
||||
}
|
||||
container.innerHTML = "";
|
||||
for (const [key, value] of Object.entries(results)) {
|
||||
const row = document.createElement("div");
|
||||
row.className = "search-result";
|
||||
row.innerHTML = `
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
${key}
|
||||
</div>
|
||||
|
||||
<div class="col-4 ${ui.event_status_class(value)}">
|
||||
${value}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
container.appendChild(row);
|
||||
}
|
||||
}
|
||||
|
||||
onclick_auth_button() {
|
||||
if (is_authenticated) {
|
||||
auth.logout().then(() => {
|
||||
ui.update_auth_button()
|
||||
})
|
||||
} else {
|
||||
auth.login().then(() => {
|
||||
ui.update_auth_button()
|
||||
})
|
||||
}
|
||||
ui.update_auth_button()
|
||||
}
|
||||
|
||||
update_auth_button() {
|
||||
const button = document.getElementById("auth_button");
|
||||
|
||||
if (is_authenticated) {
|
||||
button.innerText = "Logged In";
|
||||
button.className = "button default-dm success";
|
||||
} else {
|
||||
button.innerText = "Logged Out";
|
||||
button.className = "button default-dm secondary";
|
||||
}
|
||||
}
|
||||
|
||||
event_status_class(value) {
|
||||
switch (value.toUpperCase()) {
|
||||
case "ALLOWED":
|
||||
return "status-good";
|
||||
case "IGNORED":
|
||||
return "status-warning";
|
||||
default:
|
||||
return "status-bad";
|
||||
}
|
||||
}
|
||||
|
||||
update_metrics(metrics){
|
||||
this.blocked_domains.innerHTML = metrics.blocked_domain_count;
|
||||
|
||||
let date
|
||||
let pretty_length
|
||||
if(!metrics.last_update_complete_time){
|
||||
date = "No update since last restart"
|
||||
pretty_length = ""
|
||||
} else {
|
||||
date = new Date(metrics.last_update_complete_time * 1000).toLocaleString();
|
||||
pretty_length = `${Math.round(metrics.last_update_length)} seconds`
|
||||
}
|
||||
this.last_update_complete_time.innerHTML = date;
|
||||
this.last_update_duration.innerHTML = pretty_length;
|
||||
|
||||
let timer_value = ""
|
||||
switch (metrics.TIMER_STATE) {
|
||||
case 0:
|
||||
timer_value = "RUNNING"
|
||||
window.timer_running = true;
|
||||
break;
|
||||
case 1:
|
||||
timer_value = "STOPPING"
|
||||
window.timer_running = true;
|
||||
break;
|
||||
case 2:
|
||||
timer_value = "STOPPED"
|
||||
window.timer_running = false;
|
||||
break;
|
||||
}
|
||||
this.timer_status.innerHTML = timer_value;
|
||||
this.timer_status.className = timer_running ? "status-good" : "status-warning";
|
||||
this.sync_timer_buttons()
|
||||
|
||||
let overall_status = ""
|
||||
switch (metrics.MISP_STATE) {
|
||||
case 0:
|
||||
overall_status = "IDLE"
|
||||
break;
|
||||
case 1:
|
||||
overall_status = "FETCHING"
|
||||
break;
|
||||
case 2:
|
||||
overall_status = "UPDATING"
|
||||
break;
|
||||
case 3:
|
||||
overall_status = "RELOADING"
|
||||
break;
|
||||
case 4:
|
||||
overall_status = "ERROR"
|
||||
break;
|
||||
}
|
||||
this.overall_status.innerHTML = overall_status;
|
||||
}
|
||||
|
||||
render_false_positive_controls(domain_data) {
|
||||
document.getElementById("false_positive_actions").style.display = "block";
|
||||
const always_allow_btn = document.getElementById("always_allow_btn");
|
||||
|
||||
always_allow_btn.innerText = domain_data.always_allow ? "Enabled" : "Disabled";
|
||||
always_allow_btn.className = domain_data.always_allow ? "button default-dm success" : "button default-dm secondary";
|
||||
always_allow_btn.dataset.domain = domain_data.domain;
|
||||
always_allow_btn.dataset.always_allow = domain_data.always_allow;
|
||||
|
||||
always_allow_btn.addEventListener("click", ui.onclick_always_allow_btn)
|
||||
|
||||
ui.event_container.innerHTML = "";
|
||||
|
||||
domain_data.events.forEach(event => {
|
||||
const button = document.createElement("button");
|
||||
|
||||
button.className = `button event-btn ${event.ignored ? "default-dm warning" : "default-dm danger" }`;
|
||||
button.innerText = event.id;
|
||||
button.dataset.id = event.id;
|
||||
button.dataset.ignored = event.ignored
|
||||
button.dataset.domain = domain_data.domain;
|
||||
|
||||
ui.event_container.appendChild(button);
|
||||
});
|
||||
}
|
||||
|
||||
async onclick_manual_update_btn() {
|
||||
ui.set_loading("manual_update_btn", true);
|
||||
try {
|
||||
const data = await api.manual_update()
|
||||
if(data.state==="Starting"){
|
||||
ui.open_modal(
|
||||
"Update Triggered",
|
||||
"Manual update process successfully started."
|
||||
);
|
||||
} else {
|
||||
throw new Error()
|
||||
}
|
||||
} catch (error) {
|
||||
ui.open_modal(
|
||||
"Update Failed",
|
||||
"Failed to trigger update process."
|
||||
);
|
||||
} finally {
|
||||
ui.set_loading("manual_update_btn", false);
|
||||
}
|
||||
}
|
||||
|
||||
async onclick_start_timer_btn() {
|
||||
ui.set_loading("start_timer_btn", true);
|
||||
try {
|
||||
const data = await api.start_timer()
|
||||
api.update_metrics()
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
ui.set_loading("start_timer_btn", false);
|
||||
}
|
||||
}
|
||||
|
||||
async onclick_stop_timer_btn() {
|
||||
ui.set_loading("stop_timer_btn", true);
|
||||
try {
|
||||
const data = await api.stop_timer()
|
||||
api.update_metrics()
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
ui.set_loading("stop_timer_btn", false);
|
||||
}
|
||||
}
|
||||
|
||||
async onclick_domain_search_btn(){
|
||||
ui.set_loading("domain_search_btn", true);
|
||||
try {
|
||||
const data = await api.domain_search()
|
||||
ui.render_search_results(data)
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
ui.set_loading("domain_search_btn", false);
|
||||
}
|
||||
}
|
||||
|
||||
async load_false_positive_controls(){
|
||||
ui.set_loading("false_positive_load_btn", true);
|
||||
try {
|
||||
const data = await api.load_domain_fp()
|
||||
const parsed_data= {
|
||||
domain: data.domain,
|
||||
always_allow: data.always_allowed,
|
||||
events: []
|
||||
}
|
||||
|
||||
data.events.forEach(event => {
|
||||
parsed_data.events.push({"id": event, "ignored": data.ignored_events.includes(event)});
|
||||
})
|
||||
|
||||
ui.render_false_positive_controls(parsed_data)
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
ui.set_loading("false_positive_load_btn", false);
|
||||
}
|
||||
}
|
||||
|
||||
async onclick_false_positive_load_btn(){
|
||||
await ui.load_false_positive_controls()
|
||||
}
|
||||
|
||||
async onclick_event_button(event){
|
||||
if (event.target === event.currentTarget) return;
|
||||
const event_id = event.target.dataset.id;
|
||||
const domain = event.target.dataset.domain;
|
||||
const ignored = event.target.dataset.ignored === "true";
|
||||
try {
|
||||
if(ignored){
|
||||
await api.reinstate_event(domain, event_id)
|
||||
} else {
|
||||
await api.ignore_event(domain, event_id)
|
||||
}
|
||||
await ui.load_false_positive_controls()
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async onclick_always_allow_btn(event){
|
||||
const domain = event.target.dataset.domain;
|
||||
const always_allowed = event.target.dataset.always_allow === "true";
|
||||
try {
|
||||
await api.change_always_allow(domain, always_allowed)
|
||||
await ui.load_false_positive_controls()
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
onclick_modal_button() {
|
||||
document.getElementById("modal_backdrop").style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
class API {
|
||||
constructor() {
|
||||
const self = this
|
||||
this.update_metrics()
|
||||
this.metrics_timer = setInterval(self.update_metrics, 5000)
|
||||
}
|
||||
|
||||
update_metrics() {
|
||||
axios.get(`misp/dashboard_metrics`)
|
||||
.then(response => {
|
||||
ui.update_metrics(response.data);
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
async manual_update(published_timestamp = null){
|
||||
const user = await auth.get_user()
|
||||
return axios.put(
|
||||
`misp/manual_update`,
|
||||
{
|
||||
"published_timestamp": published_timestamp
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Bearer ${user?.access_token}`
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
.then(response => {
|
||||
return response.data
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
async start_timer(){
|
||||
const user = await auth.get_user()
|
||||
return axios.put(
|
||||
`control/start_timer`,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Bearer ${user?.access_token}`
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
.then(response => {
|
||||
return response.data
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
async stop_timer(){
|
||||
const user = await auth.get_user()
|
||||
return axios.put(
|
||||
`control/stop_timer`,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Bearer ${user?.access_token}`
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
.then(response => {
|
||||
return response.data
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
async domain_search(){
|
||||
const user = await auth.get_user()
|
||||
const search_term = document.getElementById("domain_search_input").value
|
||||
return axios.get(
|
||||
`misp/domain/search?domain=${search_term}`,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Bearer ${user?.access_token}`
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
.then(response => {
|
||||
return response.data
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
async load_domain_fp(){
|
||||
const user = await auth.get_user()
|
||||
const search_term = document.getElementById("false_positive_domain").value
|
||||
return axios.get(
|
||||
`/misp/domain/details/${search_term}`,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Bearer ${user?.access_token}`
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
.then(response => {
|
||||
return response.data
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
async ignore_event(domain, event_id){
|
||||
const user = await auth.get_user()
|
||||
return axios.patch(
|
||||
`/misp/domain/events/${domain}/ignore?event=${event_id}`,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Bearer ${user?.access_token}`
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
.then(response => {
|
||||
return response.data
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
async reinstate_event(domain, event_id){
|
||||
const user = await auth.get_user()
|
||||
return axios.patch(
|
||||
`/misp/domain/events/${domain}/reinstate?event=${event_id}`,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Bearer ${user?.access_token}`
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
.then(response => {
|
||||
return response.data
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
async change_always_allow(domain, currently_allowed){
|
||||
const user = await auth.get_user()
|
||||
return axios.patch(
|
||||
`/misp/domain/always_allowed/${domain}?allow=${!currently_allowed}`,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Bearer ${user?.access_token}`
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
.then(response => {
|
||||
return response.data
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
window.ui = new UI()
|
||||
window.auth = new Auth()
|
||||
window.api = new API()
|
||||
|
||||
window.timer_running = false;
|
||||
|
||||
ui.sync_timer_buttons();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue